summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/inputflinger/dispatcher/Android.bp1
-rw-r--r--services/inputflinger/dispatcher/DragState.cpp38
-rw-r--r--services/inputflinger/dispatcher/DragState.h45
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp74
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h5
-rw-r--r--services/inputflinger/dispatcher/TouchState.cpp2
-rw-r--r--services/inputflinger/dispatcher/TouchState.h5
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp77
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