Allow touches to slide out of the navigation bar.
Change-Id: I73cabba3d62f47829bf6217700ace56a27c42b1d
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index b0181bb..6b6aee3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -685,7 +685,19 @@
// ----- HIDDEN FLAGS.
// These start at the high bit and go down.
-
+
+ /** Window flag: Enable touches to slide out of a window into neighboring
+ * windows in mid-gesture instead of being captured for the duration of
+ * the gesture.
+ *
+ * This flag changes the behavior of touch focus for this window only.
+ * Touches can slide out of the window but they cannot necessarily slide
+ * back in (unless the other window with touch focus permits it).
+ *
+ * {@hide}
+ */
+ public static final int FLAG_SLIPPERY = 0x04000000;
+
/**
* Flag for a window belonging to an activity that responds to {@link KeyEvent#KEYCODE_MENU}
* and therefore needs a Menu key. For devices where Menu is a physical button this flag is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3d15a1d..687de07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -349,7 +349,8 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.setTitle("NavigationBar");
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index eff65c2..28df3fb 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1412,6 +1412,50 @@
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
+
+ // Check whether touches should slip outside of the current foreground window.
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE
+ && entry->pointerCount == 1
+ && mTempTouchState.isSlippery()) {
+ const MotionSample* sample = &entry->firstSample;
+ int32_t x = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+
+ const InputWindow* oldTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+ const InputWindow* newTouchedWindow = findTouchedWindowAtLocked(x, y);
+ if (oldTouchedWindow != newTouchedWindow && newTouchedWindow) {
+#if DEBUG_FOCUS
+ LOGD("Touch is slipping out of window %s into window %s.",
+ oldTouchedWindow->name.string(), newTouchedWindow->name.string());
+#endif
+ // Make a slippery exit from the old window.
+ mTempTouchState.addOrUpdateWindow(oldTouchedWindow,
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
+
+ // Make a slippery entrance into the new window.
+ if (newTouchedWindow->supportsSplitTouch()) {
+ isSplit = true;
+ }
+
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND
+ | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
+ }
+ if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ BitSet32 pointerIds;
+ if (isSplit) {
+ pointerIds.markBit(entry->pointerProperties[0].id);
+ }
+ mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
+
+ // Split the batch here so we send exactly one sample.
+ *outSplitBatchAfterSample = &entry->firstSample;
+ }
+ }
}
if (newHoverWindow != mLastHoverWindow) {
@@ -1884,6 +1928,10 @@
resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
@@ -1985,6 +2033,10 @@
action = AMOTION_EVENT_ACTION_HOVER_EXIT;
} else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
action = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ action = AMOTION_EVENT_ACTION_CANCEL;
+ } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+ action = AMOTION_EVENT_ACTION_DOWN;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -4386,6 +4438,9 @@
TouchedWindow& touchedWindow = windows.editItemAt(i);
if (touchedWindow.window == window) {
touchedWindow.targetFlags |= targetFlags;
+ if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
+ }
touchedWindow.pointerIds.value |= pointerIds.value;
return;
}
@@ -4403,7 +4458,8 @@
void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
for (size_t i = 0 ; i < windows.size(); ) {
TouchedWindow& window = windows.editItemAt(i);
- if (window.targetFlags & InputTarget::FLAG_DISPATCH_AS_IS) {
+ if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
+ | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
i += 1;
@@ -4413,15 +4469,32 @@
}
}
-const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() const {
for (size_t i = 0; i < windows.size(); i++) {
- if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
- return windows[i].window;
+ const TouchedWindow& window = windows.itemAt(i);
+ if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ return window.window;
}
}
return NULL;
}
+bool InputDispatcher::TouchState::isSlippery() const {
+ // Must have exactly one foreground window.
+ bool haveSlipperyForegroundWindow = false;
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& window = windows.itemAt(i);
+ if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ if (haveSlipperyForegroundWindow
+ || !(window.window->layoutParamsFlags & InputWindow::FLAG_SLIPPERY)) {
+ return false;
+ }
+ haveSlipperyForegroundWindow = true;
+ }
+ }
+ return haveSlipperyForegroundWindow;
+}
+
// --- InputDispatcherThread ---
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 39d4aeb..676d162 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -121,11 +121,23 @@
* The event is transmuted into ACTION_HOVER_ENTER. */
FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
+ /* This flag indicates that the event should be canceled.
+ * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
+ * outside of a window. */
+ FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
+
+ /* This flag indicates that the event should be dispatched as an initial down.
+ * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
+ * into a new window. */
+ FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
+
/* Mask for all dispatch modes. */
FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
| FLAG_DISPATCH_AS_OUTSIDE
| FLAG_DISPATCH_AS_HOVER_ENTER
- | FLAG_DISPATCH_AS_HOVER_EXIT,
+ | FLAG_DISPATCH_AS_HOVER_EXIT
+ | FLAG_DISPATCH_AS_SLIPPERY_EXIT
+ | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
};
// The input channel to be targeted.
@@ -950,9 +962,10 @@
~TouchState();
void reset();
void copyFrom(const TouchState& other);
- void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+ void addOrUpdateWindow(const InputWindow* window,int32_t targetFlags, BitSet32 pointerIds);
void filterNonAsIsTouchWindows();
- const InputWindow* getFirstForegroundWindow();
+ const InputWindow* getFirstForegroundWindow() const;
+ bool isSlippery() const;
};
TouchState mTouchState;
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index d7d819d..93c9b5f 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -80,6 +80,10 @@
FLAG_TURN_SCREEN_ON = 0x00200000,
FLAG_DISMISS_KEYGUARD = 0x00400000,
FLAG_SPLIT_TOUCH = 0x00800000,
+ FLAG_HARDWARE_ACCELERATED = 0x01000000,
+ FLAG_HARDWARE_ACCELERATED_SYSTEM = 0x02000000,
+ FLAG_SLIPPERY = 0x04000000,
+ FLAG_NEEDS_MENU_KEY = 0x08000000,
FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
FLAG_COMPATIBLE_WINDOW = 0x20000000,
FLAG_SYSTEM_ERROR = 0x40000000,