diff options
| -rw-r--r-- | services/inputflinger/dispatcher/Android.bp | 1 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/DragState.cpp | 38 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/DragState.h | 45 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 74 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 5 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/TouchState.cpp | 2 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/TouchState.h | 5 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 77 |
8 files changed, 213 insertions, 34 deletions
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 393f649746..9750ef9833 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -42,6 +42,7 @@ filegroup { "InputTarget.cpp", "Monitor.cpp", "TouchState.cpp", + "DragState.cpp", ], } diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp new file mode 100644 index 0000000000..2e2df43009 --- /dev/null +++ b/services/inputflinger/dispatcher/DragState.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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. + */ + +#include "DragState.h" +#include <android-base/stringprintf.h> +#include <input/InputWindow.h> + +using android::InputWindowHandle; +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +void DragState::dump(std::string& dump, const char* prefix) { + dump += prefix + StringPrintf("Drag Window: %s\n", dragWindow->getName().c_str()); + if (dragHoverWindowHandle) { + dump += prefix + + StringPrintf("Drag Hover Window: %s\n", dragHoverWindowHandle->getName().c_str()); + } + dump += prefix + StringPrintf("isStartDrag: %s\n", isStartDrag ? "true" : "false"); + dump += prefix + + StringPrintf("isStylusButtonDownAtStart: %s\n", + isStylusButtonDownAtStart ? "true" : "false"); +} + +} // namespace android::inputdispatcher
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h new file mode 100644 index 0000000000..06453d8eee --- /dev/null +++ b/services/inputflinger/dispatcher/DragState.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H + +#include <utils/RefBase.h> +#include <string> + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { +struct DragState { + DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {} + void dump(std::string& dump, const char* prefix = ""); + + // The window being dragged. + const sp<InputWindowHandle> dragWindow; + // The last drag hover window which could receive the drag event. + sp<InputWindowHandle> dragHoverWindowHandle; + // Indicates the if received first event to check for button state. + bool isStartDrag = false; + // Indicate if the stylus button is down at the start of the drag. + bool isStylusButtonDownAtStart = false; +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 397a9d78e5..697739688b 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -961,7 +961,7 @@ 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)) { + if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) { continue; } const InputWindowInfo* windowInfo = windowHandle->getInfo(); @@ -2061,7 +2061,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( goto Failed; } - addDragEventLocked(entry, tempTouchState); + addDragEventLocked(entry); // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && @@ -2318,39 +2318,61 @@ Failed: return injectionResult; } -void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) { - if (entry.pointerCount != 1 || !state.dragWindow) { +void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { + const sp<InputWindowHandle> dropWindow = + findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, + false /*addOutsideTargets*/, false /*addPortalWindows*/, + true /*ignoreDragWindow*/); + if (dropWindow) { + vec2 local = dropWindow->getInfo()->transform.transform(x, y); + notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y); + } + mDragState.reset(); +} + +void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { + if (entry.pointerCount != 1 || !mDragState) { return; } + if (!mDragState->isStartDrag) { + mDragState->isStartDrag = true; + mDragState->isStylusButtonDownAtStart = + (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0; + } + 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) { + // Handle the special case : stylus button no longer pressed. + bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0; + if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) { + finishDragAndDrop(entry.displayId, x, y); + return; + } + const sp<InputWindowHandle> hoverWindowHandle = - findTouchedWindowAtLocked(entry.displayId, x, y, &state, + findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, 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); + if (hoverWindowHandle != mDragState->dragHoverWindowHandle && + !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) { + if (mDragState->dragHoverWindowHandle != nullptr) { + enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/, + entry); } - state.dragHoverWindowHandle = hoverWindowHandle; + mDragState->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) { - if (state.dragHoverWindowHandle && maskedAction == AMOTION_EVENT_ACTION_UP) { - vec2 local = state.dragHoverWindowHandle->getInfo()->transform.transform(x, y); - notifyDropWindowLocked(state.dragHoverWindowHandle->getToken(), local.x, local.y); - } - state.dragWindow = nullptr; - state.dragHoverWindowHandle = nullptr; + } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { + finishDragAndDrop(entry.displayId, x, y); + } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + mDragState.reset(); } } @@ -4445,13 +4467,12 @@ void InputDispatcher::setInputWindowsLocked( } } - // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we + // 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) == + if (mDragState && + std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) == windowHandles.end()) { - state.dragWindow = nullptr; - state.dragHoverWindowHandle = nullptr; + mDragState.reset(); } } @@ -4690,7 +4711,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Store the dragging window. if (isDragDrop) { - state.dragWindow = toWindowHandle; + mDragState = std::make_unique<DragState>(toWindowHandle); } found = true; @@ -4833,6 +4854,11 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "TouchStates: <no displays touched>\n"; } + if (mDragState) { + dump += StringPrintf(INDENT "DragState:\n"); + mDragState->dump(dump, INDENT2); + } + if (!mWindowHandlesByDisplay.empty()) { for (auto& it : mWindowHandlesByDisplay) { const std::vector<sp<InputWindowHandle>> windowHandles = it.second; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 593ec23e23..5708face4b 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -19,6 +19,7 @@ #include "AnrTracker.h" #include "CancelationOptions.h" +#include "DragState.h" #include "Entry.h" #include "FocusResolver.h" #include "InjectionState.h" @@ -340,6 +341,7 @@ private: REQUIRES(mLock); std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); + std::unique_ptr<DragState> mDragState GUARDED_BY(mLock); // Focused applications. std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>> @@ -499,7 +501,8 @@ private: 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); + void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock); + void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock); struct TouchOcclusionInfo { bool hasBlockingOcclusion; diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 4165f4934c..81b3cf025b 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -49,8 +49,6 @@ 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 d7a561c5ae..623c6a824f 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -41,11 +41,6 @@ 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/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index cedda6e91c..485fc99b1b 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1165,8 +1165,8 @@ public: return *this; } - MotionEventBuilder& buttonState(int32_t actionButton) { - mActionButton = actionButton; + MotionEventBuilder& buttonState(int32_t buttonState) { + mButtonState = buttonState; return *this; } @@ -4761,6 +4761,32 @@ protected: mWindow->consumeMotionCancel(); mDragWindow->consumeMotionDown(); } + + // Start performing drag, we will create a drag window and transfer touch to it. + void performStylusDrag() { + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_STYLUS) + .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(PointerBuilder(0, + AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(50) + .y(50)) + .build())); + 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) { @@ -4833,4 +4859,51 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { + performStylusDrag(); + + // Move on window and keep button pressed. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(50) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + + // Move to another window and release button, expect to drop item. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken()); + + // nothing to the window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); +} + } // namespace android::inputdispatcher |