Add support for mouse hover and scroll wheel.
Dispatch ACTION_HOVER_MOVE and ACTION_SCROLL through the View
hierarchy as onGenericTouchEvent. Pointer events dispatched
this way are delivered to the view under the pointer. Non-pointer
events continue to be delivered to the focused view.
Added scroll wheel support to AbsListView, ScrollView,
HorizontalScrollView and WebView. Shift+VSCROLL is translated
to HSCROLL as appropriate.
Added logging of new pointer events in PointerLocationView.
Fixed a problem in EventHub when a USB device is removed that
resulted in a long stream of ENODEV errors being logged until INotify
noticed the device was gone.
Note that the new events are not supported by wallpapers at this time
because the wallpaper engine only delivers touch events.
Make all mouse buttons behave identically. (Effectively we only
support one button.)
Change-Id: I9ab445ffb63c813fcb07db6693987b02475f3756
diff --git a/api/current.xml b/api/current.xml
index 872e627..ad95c94 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -217957,6 +217957,17 @@
visibility="public"
>
</field>
+<field name="ACTION_SCROLL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_UP"
type="int"
transient="false"
@@ -224280,6 +224291,19 @@
<parameter name="l" type="android.view.View.OnFocusChangeListener">
</parameter>
</method>
+<method name="setOnGenericMotionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="l" type="android.view.View.OnGenericMotionListener">
+</parameter>
+</method>
<method name="setOnKeyListener"
return="void"
abstract="false"
@@ -225983,6 +226007,29 @@
</parameter>
</method>
</interface>
+<interface name="View.OnGenericMotionListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onGenericMotion"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+</interface>
<interface name="View.OnKeyListener"
abstract="true"
static="true"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 67e4806..9e72c1b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2122,24 +2122,21 @@
* Called when a generic motion event was not handled by any of the
* views inside of the activity.
* <p>
- * Generic motion events are dispatched to the focused view to describe
- * the motions of input devices such as joysticks. The
+ * Generic motion events describe joystick movements, mouse hovers, track pad
+ * touches, scroll wheel movements and other input events. The
* {@link MotionEvent#getSource() source} of the motion event specifies
* the class of input that was received. Implementations of this method
* must examine the bits in the source before processing the event.
* The following code example shows how this is done.
+ * </p><p>
+ * Generic motion events with source class
+ * {@link android.view.InputDevice#SOURCE_CLASS_POINTER}
+ * are delivered to the view under the pointer. All other generic motion events are
+ * delivered to the focused view.
+ * </p><p>
+ * See {@link View#onGenericMotionEvent(MotionEvent)} for an example of how to
+ * handle this event.
* </p>
- * <code>
- * public boolean onGenericMotionEvent(MotionEvent event) {
- * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- * float x = event.getX();
- * float y = event.getY();
- * // process the joystick motion
- * return true;
- * }
- * return super.onGenericMotionEvent(event);
- * }
- * </code>
*
* @param event The generic motion event being processed.
*
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index cc4fefc3..087753b 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -627,24 +627,21 @@
* Called when a generic motion event was not handled by any of the
* views inside of the dialog.
* <p>
- * Generic motion events are dispatched to the focused view to describe
- * the motions of input devices such as joysticks. The
+ * Generic motion events describe joystick movements, mouse hovers, track pad
+ * touches, scroll wheel movements and other input events. The
* {@link MotionEvent#getSource() source} of the motion event specifies
* the class of input that was received. Implementations of this method
* must examine the bits in the source before processing the event.
* The following code example shows how this is done.
+ * </p><p>
+ * Generic motion events with source class
+ * {@link android.view.InputDevice#SOURCE_CLASS_POINTER}
+ * are delivered to the view under the pointer. All other generic motion events are
+ * delivered to the focused view.
+ * </p><p>
+ * See {@link View#onGenericMotionEvent(MotionEvent)} for an example of how to
+ * handle this event.
* </p>
- * <code>
- * public boolean onGenericMotionEvent(MotionEvent event) {
- * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- * float x = event.getX();
- * float y = event.getY();
- * // process the joystick motion
- * return true;
- * }
- * return super.onGenericMotionEvent(event);
- * }
- * </code>
*
* @param event The generic motion event being processed.
*
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd39714..20661d7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -460,16 +460,17 @@
}
private void dispatchPointer(MotionEvent event) {
- synchronized (mLock) {
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- mPendingMove = event;
- } else {
- mPendingMove = null;
+ if (event.isTouchEvent()) {
+ synchronized (mLock) {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ mPendingMove = event;
+ } else {
+ mPendingMove = null;
+ }
}
+ Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
+ mCaller.sendMessage(msg);
}
-
- Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
- mCaller.sendMessage(msg);
}
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index cc37a28..a26dd04 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -40,6 +40,12 @@
* by a motion event with {@link #ACTION_UP} or when gesture is canceled
* with {@link #ACTION_CANCEL}.
* </p><p>
+ * Some pointing devices such as mice may support vertical and/or horizontal scrolling.
+ * A scroll event is reported as a generic motion event with {@link #ACTION_SCROLL} that
+ * includes the relative scroll offset in the {@link #AXIS_VSCROLL} and
+ * {@link #AXIS_HSCROLL} axes. See {@link #getAxisValue(int)} for information
+ * about retrieving these additional axes.
+ * </p><p>
* On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL},
* the pointer coordinates specify relative movements as X/Y deltas.
* A trackball gesture consists of a sequence of movements described by motion
@@ -51,6 +57,8 @@
* The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds
* to the center position. More information about the set of available axes and the
* range of motion can be obtained using {@link InputDevice#getMotionRange}.
+ * Some common joystick axes are {@link #AXIS_X}, {@link #AXIS_Y},
+ * {@link #AXIS_HAT_X}, {@link #AXIS_HAT_Y}, {@link #AXIS_Z} and {@link #AXIS_RZ}.
* </p><p>
* Motion events always report movements for all pointers at once. The number
* of pointers only ever changes by one as individual pointers go up and down,
@@ -163,10 +171,30 @@
* is not down (unlike {@link #ACTION_MOVE}). The motion contains the most
* recent point, as well as any intermediate points since the last
* hover move event.
+ * <p>
+ * This action is not a touch event so it is delivered to
+ * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link View#onTouchEvent(MotionEvent)}.
+ * </p>
*/
public static final int ACTION_HOVER_MOVE = 7;
/**
+ * Constant for {@link #getAction}: The motion event contains relative
+ * vertical and/or horizontal scroll offsets. Use {@link #getAxisValue(int)}
+ * to retrieve the information from {@link #AXIS_VSCROLL} and {@link #AXIS_HSCROLL}.
+ * The pointer may or may not be down when this event is dispatched.
+ * This action is always delivered to the winder under the pointer, which
+ * may not be the window currently touched.
+ * <p>
+ * This action is not a touch event so it is delivered to
+ * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link View#onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+ public static final int ACTION_SCROLL = 8;
+
+ /**
* Bits in the action code that represent a pointer index, used with
* {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting
* down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer
@@ -483,7 +511,7 @@
* <p>
* <ul>
* <li>For a mouse, reports the relative movement of the vertical scroll wheel.
- * The value is normalized to a range from -1.0 (up) to 1.0 (down).
+ * The value is normalized to a range from -1.0 (down) to 1.0 (up).
* </ul>
* </p><p>
* This axis should be used to scroll views vertically.
@@ -1237,6 +1265,32 @@
}
/**
+ * Returns true if this motion event is a touch event.
+ * <p>
+ * Specifically excludes pointer events with action {@link #ACTION_HOVER_MOVE}
+ * or {@link #ACTION_SCROLL} because they are not actually touch events
+ * (the pointer is not down).
+ * </p>
+ * @return True if this motion event is a touch event.
+ * @hide
+ */
+ public final boolean isTouchEvent() {
+ if ((getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_OUTSIDE:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Gets the motion event flags.
*
* @see #FLAG_WINDOW_IS_OBSCURED
@@ -2174,10 +2228,14 @@
return "ACTION_UP";
case ACTION_CANCEL:
return "ACTION_CANCEL";
+ case ACTION_OUTSIDE:
+ return "ACTION_OUTSIDE";
case ACTION_MOVE:
return "ACTION_MOVE";
case ACTION_HOVER_MOVE:
return "ACTION_HOVER_MOVE";
+ case ACTION_SCROLL:
+ return "ACTION_SCROLL";
}
int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
switch (action & ACTION_MASK) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 78eb2e5..01bc2df 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -57,6 +57,7 @@
import android.util.PoolableManager;
import android.util.Pools;
import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
@@ -2128,6 +2129,8 @@
private OnTouchListener mOnTouchListener;
+ private OnGenericMotionListener mOnGenericMotionListener;
+
private OnDragListener mOnDragListener;
private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
@@ -2257,6 +2260,11 @@
public static final int DRAG_FLAG_GLOBAL = 1;
/**
+ * Vertical scroll factor cached by {@link #getVerticalScrollFactor}.
+ */
+ private float mVerticalScrollFactor;
+
+ /**
* Position of the vertical scroll bar.
*/
private int mVerticalScrollbarPosition;
@@ -3167,6 +3175,14 @@
}
/**
+ * Register a callback to be invoked when a generic motion event is sent to this view.
+ * @param l the generic motion listener to attach to this view
+ */
+ public void setOnGenericMotionListener(OnGenericMotionListener l) {
+ mOnGenericMotionListener = l;
+ }
+
+ /**
* Register a drag event listener callback object for this View. The parameter is
* an implementation of {@link android.view.View.OnDragListener}. To send a drag event to a
* View, the system calls the
@@ -4624,16 +4640,47 @@
}
/**
- * Pass a generic motion event down to the focused view.
+ * Dispatch a generic motion event.
+ * <p>
+ * Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
+ * are delivered to the view under the pointer. All other generic motion events are
+ * delivered to the focused view.
+ * </p>
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+ && mOnGenericMotionListener.onGenericMotion(this, event)) {
+ return true;
+ }
+
return onGenericMotionEvent(event);
}
/**
+ * Dispatch a pointer event.
+ * <p>
+ * Dispatches touch related pointer events to {@link #onTouchEvent} and all
+ * other events to {@link #onGenericMotionEvent}. This separation of concerns
+ * reinforces the invariant that {@link #onTouchEvent} is really about touches
+ * and should not be expected to handle other pointing device features.
+ * </p>
+ *
+ * @param event The motion event to be dispatched.
+ * @return True if the event was handled by the view, false otherwise.
+ * @hide
+ */
+ public final boolean dispatchPointerEvent(MotionEvent event) {
+ if (event.isTouchEvent()) {
+ return dispatchTouchEvent(event);
+ } else {
+ return dispatchGenericMotionEvent(event);
+ }
+ }
+
+ /**
* Called when the window containing this view gains or loses window focus.
* ViewGroups should override to route to their children.
*
@@ -5142,20 +5189,34 @@
/**
* Implement this method to handle generic motion events.
* <p>
- * Generic motion events are dispatched to the focused view to describe
- * the motions of input devices such as joysticks. The
+ * Generic motion events describe joystick movements, mouse hovers, track pad
+ * touches, scroll wheel movements and other input events. The
* {@link MotionEvent#getSource() source} of the motion event specifies
* the class of input that was received. Implementations of this method
* must examine the bits in the source before processing the event.
* The following code example shows how this is done.
+ * </p><p>
+ * Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
+ * are delivered to the view under the pointer. All other generic motion events are
+ * delivered to the focused view.
* </p>
* <code>
* public boolean onGenericMotionEvent(MotionEvent event) {
* if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- * float x = event.getX();
- * float y = event.getY();
- * // process the joystick motion
- * return true;
+ * if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ * // process the joystick movement...
+ * return true;
+ * }
+ * }
+ * if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ * switch (event.getAction()) {
+ * case MotionEvent.ACTION_HOVER_MOVE:
+ * // process the mouse hover movement...
+ * return true;
+ * case MotionEvent.ACTION_SCROLL:
+ * // process the scroll wheel movement...
+ * return true;
+ * }
* }
* return super.onGenericMotionEvent(event);
* }
@@ -11653,6 +11714,37 @@
}
/**
+ * Gets a scale factor that determines the distance the view should scroll
+ * vertically in response to {@link MotionEvent#ACTION_SCROLL}.
+ * @return The vertical scroll scale factor.
+ * @hide
+ */
+ protected float getVerticalScrollFactor() {
+ if (mVerticalScrollFactor == 0) {
+ TypedValue outValue = new TypedValue();
+ if (!mContext.getTheme().resolveAttribute(
+ com.android.internal.R.attr.listPreferredItemHeight, outValue, true)) {
+ throw new IllegalStateException(
+ "Expected theme to define listPreferredItemHeight.");
+ }
+ mVerticalScrollFactor = outValue.getDimension(
+ mContext.getResources().getDisplayMetrics());
+ }
+ return mVerticalScrollFactor;
+ }
+
+ /**
+ * Gets a scale factor that determines the distance the view should scroll
+ * horizontally in response to {@link MotionEvent#ACTION_SCROLL}.
+ * @return The horizontal scroll scale factor.
+ * @hide
+ */
+ protected float getHorizontalScrollFactor() {
+ // TODO: Should use something else.
+ return getVerticalScrollFactor();
+ }
+
+ /**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height.
* A MeasureSpec is comprised of a size and a mode. There are three possible
@@ -11860,6 +11952,24 @@
}
/**
+ * Interface definition for a callback to be invoked when a generic motion event is
+ * dispatched to this view. The callback will be invoked before the generic motion
+ * event is given to the view.
+ */
+ public interface OnGenericMotionListener {
+ /**
+ * Called when a generic motion event is dispatched to a view. This allows listeners to
+ * get a chance to respond before the target view.
+ *
+ * @param v The view the generic motion event has been dispatched to.
+ * @param event The MotionEvent object containing full information about
+ * the event.
+ * @return True if the listener has consumed the event, false otherwise.
+ */
+ boolean onGenericMotion(View v, MotionEvent event);
+ }
+
+ /**
* Interface definition for a callback to be invoked when a view has been clicked and held.
*/
public interface OnLongClickListener {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a0d4263..5c06151 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1145,6 +1145,53 @@
*/
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ // Send the event to the child under the pointer.
+ final int childrenCount = mChildrenCount;
+ if (childrenCount != 0) {
+ final View[] children = mChildren;
+ final float x = event.getX();
+ final float y = event.getY();
+
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final View child = children[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
+ && child.getAnimation() == null) {
+ // Skip invisible child unless it is animating.
+ continue;
+ }
+
+ if (!isTransformedTouchPointInView(x, y, child, null)) {
+ // Scroll point is out of child's bounds.
+ continue;
+ }
+
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ final boolean handled;
+ if (!child.hasIdentityMatrix()) {
+ MotionEvent transformedEvent = MotionEvent.obtain(event);
+ transformedEvent.offsetLocation(offsetX, offsetY);
+ transformedEvent.transform(child.getInverseMatrix());
+ handled = child.dispatchGenericMotionEvent(transformedEvent);
+ transformedEvent.recycle();
+ } else {
+ event.offsetLocation(offsetX, offsetY);
+ handled = child.dispatchGenericMotionEvent(event);
+ event.offsetLocation(-offsetX, -offsetY);
+ }
+
+ if (handled) {
+ return true;
+ }
+ }
+ }
+
+ // No child handled the event. Send it to this view group.
+ return super.dispatchGenericMotionEvent(event);
+ }
+
+ // Send the event to the focused child or to this view group if it has focus.
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchGenericMotionEvent(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
@@ -1178,7 +1225,6 @@
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
@@ -1188,6 +1234,8 @@
intercepted = false;
}
} else {
+ // There are no touch targets and this action is not an initial down
+ // so this view group continues to intercept touches.
intercepted = true;
}
@@ -1548,8 +1596,6 @@
final int newAction;
if (cancel) {
newAction = MotionEvent.ACTION_CANCEL;
- } else if (oldAction == MotionEvent.ACTION_HOVER_MOVE) {
- newAction = MotionEvent.ACTION_HOVER_MOVE;
} else {
final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 39f99b8..c7b1955 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2247,7 +2247,7 @@
private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishPointerEvent(event, sendDone, false);
+ finishMotionEvent(event, sendDone, false);
return;
}
@@ -2270,7 +2270,7 @@
event.offsetLocation(0, mCurScrollY);
}
if (MEASURE_LATENCY) {
- lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+ lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
}
// Remember the touch position for possible drag-initiation.
@@ -2278,12 +2278,12 @@
mLastTouchPoint.y = event.getRawY();
// Dispatch touch to view hierarchy.
- boolean handled = mView.dispatchTouchEvent(event);
+ boolean handled = mView.dispatchPointerEvent(event);
if (MEASURE_LATENCY) {
- lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+ lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (handled) {
- finishPointerEvent(event, sendDone, true);
+ finishMotionEvent(event, sendDone, true);
return;
}
@@ -2325,23 +2325,27 @@
if (nearest != null) {
event.offsetLocation(deltas[0], deltas[1]);
event.setEdgeFlags(0);
- if (mView.dispatchTouchEvent(event)) {
- finishPointerEvent(event, sendDone, true);
+ if (mView.dispatchPointerEvent(event)) {
+ finishMotionEvent(event, sendDone, true);
return;
}
}
}
// Pointer event was unhandled.
- finishPointerEvent(event, sendDone, false);
+ finishMotionEvent(event, sendDone, false);
}
- private void finishPointerEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
event.recycle();
if (sendDone) {
finishInputEvent(handled);
}
- if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
+ if (LOCAL_LOGV || WATCH_POINTER) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ Log.i(TAG, "Done dispatching!");
+ }
+ }
}
private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
@@ -2349,7 +2353,7 @@
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishTrackballEvent(event, sendDone, false);
+ finishMotionEvent(event, sendDone, false);
return;
}
@@ -2361,7 +2365,7 @@
// touch mode here.
ensureTouchMode(false);
- finishTrackballEvent(event, sendDone, true);
+ finishMotionEvent(event, sendDone, true);
mLastTrackballTime = Integer.MIN_VALUE;
return;
}
@@ -2471,14 +2475,7 @@
// Unfortunately we can't tell whether the application consumed the keys, so
// we always consider the trackball event handled.
- finishTrackballEvent(event, sendDone, true);
- }
-
- private void finishTrackballEvent(MotionEvent event, boolean sendDone, boolean handled) {
- event.recycle();
- if (sendDone) {
- finishInputEvent(handled);
- }
+ finishMotionEvent(event, sendDone, true);
}
private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
@@ -2490,7 +2487,7 @@
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishGenericMotionEvent(event, sendDone, false);
+ finishMotionEvent(event, sendDone, false);
return;
}
@@ -2499,23 +2496,16 @@
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishGenericMotionEvent(event, sendDone, true);
+ finishMotionEvent(event, sendDone, true);
return;
}
if (isJoystick) {
// Translate the joystick event into DPAD keys and try to deliver those.
updateJoystickDirection(event, true);
- finishGenericMotionEvent(event, sendDone, true);
+ finishMotionEvent(event, sendDone, true);
} else {
- finishGenericMotionEvent(event, sendDone, false);
- }
- }
-
- private void finishGenericMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
- event.recycle();
- if (sendDone) {
- finishInputEvent(handled);
+ finishMotionEvent(event, sendDone, false);
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 06cb64e8..98fc290 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -62,6 +62,7 @@
import android.util.Log;
import android.view.Gravity;
import android.view.HardwareCanvas;
+import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -6147,6 +6148,33 @@
nativeHideCursor();
}
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ final float vscroll;
+ final float hscroll;
+ if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
+ vscroll = 0;
+ hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ } else {
+ vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ }
+ if (hscroll != 0 || vscroll != 0) {
+ final int vdelta = (int) (vscroll * getVerticalScrollFactor());
+ final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
+ if (pinScrollBy(hdelta, vdelta, true, 0)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
private long mTrackballFirstTime = 0;
private long mTrackballLastTime = 0;
private float mTrackballRemainsX = 0.0f;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index eb53e56..c2c8d16 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -43,6 +43,7 @@
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -3261,6 +3262,26 @@
}
@Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ if (mTouchMode == TOUCH_MODE_REST) {
+ final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ if (vscroll != 0) {
+ final int delta = (int) (vscroll * getVerticalScrollFactor());
+ if (trackMotionScroll(delta, delta)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mEdgeGlowTop != null) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index e26e99b..3255f6f 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -26,6 +26,7 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.FocusFinder;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -634,6 +635,40 @@
}
@Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ if (!mIsBeingDragged) {
+ final float hscroll;
+ if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
+ hscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ } else {
+ hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ }
+ if (hscroll != 0) {
+ final int delta = (int) (hscroll * getHorizontalScrollFactor());
+ final int range = getScrollRange();
+ int oldScrollX = mScrollX;
+ int newScrollX = oldScrollX + delta;
+ if (newScrollX < 0) {
+ newScrollX = 0;
+ } else if (newScrollX > range) {
+ newScrollX = range;
+ }
+ if (newScrollX != oldScrollX) {
+ super.scrollTo(newScrollX, mScrollY);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @Override
protected void onOverScrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 9932320..7aca0db 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -27,6 +27,7 @@
import android.os.StrictMode;
import android.util.AttributeSet;
import android.view.FocusFinder;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -631,6 +632,35 @@
}
@Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ if (!mIsBeingDragged) {
+ final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ if (vscroll != 0) {
+ final int delta = (int) (vscroll * getVerticalScrollFactor());
+ final int range = getScrollRange();
+ int oldScrollY = mScrollY;
+ int newScrollY = oldScrollY - delta;
+ if (newScrollY < 0) {
+ newScrollY = 0;
+ } else if (newScrollY > range) {
+ newScrollY = range;
+ }
+ if (newScrollY != oldScrollY) {
+ super.scrollTo(mScrollX, newScrollY);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @Override
protected void onOverScrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index c72d0e8..5ac903d 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -318,10 +318,56 @@
}
}
- private void logPointerCoords(MotionEvent.PointerCoords coords, int id) {
+ private void logPointerCoords(int action, int index, MotionEvent.PointerCoords coords, int id) {
+ final String prefix;
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ prefix = "DOWN";
+ break;
+ case MotionEvent.ACTION_UP:
+ prefix = "UP";
+ break;
+ case MotionEvent.ACTION_MOVE:
+ prefix = "MOVE";
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ prefix = "CANCEL";
+ break;
+ case MotionEvent.ACTION_OUTSIDE:
+ prefix = "OUTSIDE";
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
+ prefix = "DOWN";
+ } else {
+ prefix = "MOVE";
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
+ prefix = "UP";
+ } else {
+ prefix = "MOVE";
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ prefix = "HOVER MOVE";
+ break;
+ case MotionEvent.ACTION_SCROLL:
+ prefix = "SCROLL";
+ break;
+ default:
+ prefix = Integer.toString(action);
+ break;
+ }
+
Log.i(TAG, mText.clear()
.append("Pointer ").append(id + 1)
- .append(": (").append(coords.x, 3).append(", ").append(coords.y, 3)
+ .append(": ")
+ .append(prefix)
+ .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
.append(") Pressure=").append(coords.pressure, 3)
.append(" Size=").append(coords.size, 3)
.append(" TouchMajor=").append(coords.touchMajor, 3)
@@ -335,7 +381,7 @@
.toString());
}
- public void addTouchEvent(MotionEvent event) {
+ public void addPointerEvent(MotionEvent event) {
synchronized (mPointers) {
int action = event.getAction();
@@ -363,10 +409,16 @@
ps.mCurDown = false;
}
mCurDown = true;
+ mCurNumPointers = 0;
mMaxNumPointers = 0;
mVelocity.clear();
}
-
+
+ mCurNumPointers += 1;
+ if (mMaxNumPointers < mCurNumPointers) {
+ mMaxNumPointers = mCurNumPointers;
+ }
+
final int id = event.getPointerId(index);
while (NP <= id) {
PointerState ps = new PointerState();
@@ -375,49 +427,41 @@
}
if (mActivePointerId < 0 ||
- ! mPointers.get(mActivePointerId).mCurDown) {
+ !mPointers.get(mActivePointerId).mCurDown) {
mActivePointerId = id;
}
final PointerState ps = mPointers.get(id);
ps.mCurDown = true;
- if (mPrintCoords) {
- Log.i(TAG, mText.clear().append("Pointer ")
- .append(id + 1).append(": DOWN").toString());
- }
}
-
- final int NI = event.getPointerCount();
- final boolean hover = (action == MotionEvent.ACTION_HOVER_MOVE);
- mCurDown = action != MotionEvent.ACTION_UP
- && action != MotionEvent.ACTION_CANCEL
- && !hover;
- mCurNumPointers = mCurDown ? NI : 0;
- if (mMaxNumPointers < mCurNumPointers) {
- mMaxNumPointers = mCurNumPointers;
- }
+ final int NI = event.getPointerCount();
mVelocity.addMovement(event);
mVelocity.computeCurrentVelocity(1);
-
- for (int i=0; i<NI; i++) {
- final int id = event.getPointerId(i);
- final PointerState ps = hover ? null : mPointers.get(id);
- final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
- final int N = event.getHistorySize();
- for (int j=0; j<N; j++) {
- event.getHistoricalPointerCoords(i, j, coords);
+
+ final int N = event.getHistorySize();
+ for (int historyPos = 0; historyPos < N; historyPos++) {
+ for (int i = 0; i < NI; i++) {
+ final int id = event.getPointerId(i);
+ final PointerState ps = mCurDown ? mPointers.get(id) : null;
+ final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
+ event.getHistoricalPointerCoords(i, historyPos, coords);
if (mPrintCoords) {
- logPointerCoords(coords, id);
+ logPointerCoords(action, i, coords, id);
}
if (ps != null) {
- ps.addTrace(event.getHistoricalX(i, j), event.getHistoricalY(i, j));
+ ps.addTrace(coords.x, coords.y);
}
}
+ }
+ for (int i = 0; i < NI; i++) {
+ final int id = event.getPointerId(i);
+ final PointerState ps = mCurDown ? mPointers.get(id) : null;
+ final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
event.getPointerCoords(i, coords);
if (mPrintCoords) {
- logPointerCoords(coords, id);
+ logPointerCoords(action, i, coords, id);
}
if (ps != null) {
ps.addTrace(coords.x, coords.y);
@@ -425,7 +469,7 @@
ps.mYVelocity = mVelocity.getYVelocity(id);
}
}
-
+
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
@@ -435,15 +479,13 @@
final int id = event.getPointerId(index);
final PointerState ps = mPointers.get(id);
ps.mCurDown = false;
- if (mPrintCoords) {
- Log.i(TAG, mText.clear().append("Pointer ")
- .append(id + 1).append(": UP").toString());
- }
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL) {
mCurDown = false;
+ mCurNumPointers = 0;
} else {
+ mCurNumPointers -= 1;
if (mActivePointerId == id) {
mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
}
@@ -462,11 +504,20 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- addTouchEvent(event);
+ addPointerEvent(event);
return true;
}
@Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ addPointerEvent(event);
+ return true;
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @Override
public boolean onTrackballEvent(MotionEvent event) {
Log.i(TAG, "Trackball: " + event);
return super.onTrackballEvent(event);
diff --git a/native/include/android/input.h b/native/include/android/input.h
index d516037..f19e8be 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -288,6 +288,15 @@
* the last hover move event.
*/
AMOTION_EVENT_ACTION_HOVER_MOVE = 7,
+
+ /* The motion event contains relative vertical and/or horizontal scroll offsets.
+ * Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL
+ * and AMOTION_EVENT_AXIS_HSCROLL.
+ * The pointer may or may not be down when this event is dispatched.
+ * This action is always delivered to the winder under the pointer, which
+ * may not be the window currently touched.
+ */
+ AMOTION_EVENT_ACTION_SCROLL = 8,
};
/*
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index f5f4c6e..75ef762 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -284,7 +284,7 @@
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
synchronized (mLock) {
if (mPointerLocationView != null) {
- mPointerLocationView.addTouchEvent(event);
+ mPointerLocationView.addPointerEvent(event);
handled = true;
}
}
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index b31381a..25db25e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -508,6 +508,7 @@
}
// Grab the next input event.
+ bool deviceWasRemoved = false;
for (;;) {
// Consume buffered input events, if any.
if (mInputBufferIndex < mInputBufferCount) {
@@ -558,6 +559,10 @@
int32_t readSize = read(pfd.fd, mInputBufferData,
sizeof(struct input_event) * INPUT_BUFFER_SIZE);
if (readSize < 0) {
+ if (errno == ENODEV) {
+ deviceWasRemoved = true;
+ break;
+ }
if (errno != EAGAIN && errno != EINTR) {
LOGW("could not get event (errno=%d)", errno);
}
@@ -570,6 +575,13 @@
}
}
+ // Handle the case where a device has been removed but INotify has not yet noticed.
+ if (deviceWasRemoved) {
+ AutoMutex _l(mLock);
+ closeDeviceAtIndexLocked(mInputFdIndex);
+ continue; // report added or removed devices immediately
+ }
+
#if HAVE_INOTIFY
// readNotify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
@@ -580,8 +592,6 @@
}
#endif
- mInputFdIndex = 0;
-
// Poll for events. Mind the wake lock dance!
// We hold a wake lock at all times except during poll(). This works due to some
// subtle choreography. When a device driver has pending (unread) events, it acquires
@@ -602,6 +612,9 @@
usleep(100000);
}
}
+
+ // Prepare to process all of the FDs we just polled.
+ mInputFdIndex = 0;
}
}
@@ -1008,37 +1021,42 @@
for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
Device* device = mDevices[i];
if (device->path == devicePath) {
- LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
- device->path.string(), device->identifier.name.string(), device->id,
- device->fd, device->classes);
-
- for (int j=0; j<EV_SW; j++) {
- if (mSwitches[j] == device->id) {
- mSwitches[j] = 0;
- }
- }
-
- if (device->id == mBuiltInKeyboardId) {
- LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.string(), mBuiltInKeyboardId);
- mBuiltInKeyboardId = -1;
- clearKeyboardProperties(device, true);
- }
- clearKeyboardProperties(device, false);
-
- mFds.removeAt(i);
- mDevices.removeAt(i);
- device->close();
-
- device->next = mClosingDevices;
- mClosingDevices = device;
- return 0;
+ return closeDeviceAtIndexLocked(i);
}
}
- LOGE("remove device: %s not found\n", devicePath);
+ LOGV("Remove device: %s not found, device may already have been removed.", devicePath);
return -1;
}
+int EventHub::closeDeviceAtIndexLocked(int index) {
+ Device* device = mDevices[index];
+ LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
+ device->path.string(), device->identifier.name.string(), device->id,
+ device->fd, device->classes);
+
+ for (int j=0; j<EV_SW; j++) {
+ if (mSwitches[j] == device->id) {
+ mSwitches[j] = 0;
+ }
+ }
+
+ if (device->id == mBuiltInKeyboardId) {
+ LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
+ device->path.string(), mBuiltInKeyboardId);
+ mBuiltInKeyboardId = -1;
+ clearKeyboardProperties(device, true);
+ }
+ clearKeyboardProperties(device, false);
+
+ mFds.removeAt(index);
+ mDevices.removeAt(index);
+ device->close();
+
+ device->next = mClosingDevices;
+ mClosingDevices = device;
+ return 0;
+}
+
int EventHub::readNotify(int nfd) {
#ifdef HAVE_INOTIFY
int res;
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 23bb344..f7936d2 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -261,6 +261,7 @@
int openDevice(const char *devicePath);
int closeDevice(const char *devicePath);
+ int closeDeviceAtIndexLocked(int index);
int scanDir(const char *dirname);
int readNotify(int nfd);
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 2e3f0bd..c064a9c 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -116,6 +116,7 @@
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
@@ -480,8 +481,7 @@
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if ((motionEntry->action == AMOTION_EVENT_ACTION_DOWN
- || motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE)
+ if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
&& mInputTargetWaitApplication != NULL) {
@@ -1180,7 +1180,8 @@
&& (mTouchState.deviceId != entry->deviceId
|| mTouchState.source != entry->source);
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+ || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (wrongDevice && !down) {
mTempTouchState.copyFrom(mTouchState);
@@ -1205,8 +1206,9 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
|| (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- /* Case 1: New splittable pointer going down. */
+ || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+ || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].
@@ -1380,8 +1382,10 @@
// If this is the first pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the duration
// of the touch gesture.
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
+ // engine only supports touch events. We would need to add a mechanism similar
+ // to View.onGenericMotionEvent to enable wallpapers to handle these events.
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
if (foregroundWindow->hasWallpaper) {
for (size_t i = 0; i < mWindows.size(); i++) {
@@ -1423,7 +1427,7 @@
|| maskedAction == AMOTION_EVENT_ACTION_CANCEL
|| maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
// All pointers up or canceled.
- mTempTouchState.reset();
+ mTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (mTouchState.down) {
@@ -1432,6 +1436,7 @@
LOGD("Pointer down received while already down.");
#endif
}
+ mTouchState.copyFrom(mTempTouchState);
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
if (isSplit) {
@@ -1450,10 +1455,13 @@
i += 1;
}
}
+ mTouchState.copyFrom(mTempTouchState);
+ } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ // Discard temporary touch state since it was only valid for this action.
+ } else {
+ // Save changes to touch state as-is for all other actions.
+ mTouchState.copyFrom(mTempTouchState);
}
-
- // Save changes to touch state.
- mTouchState.copyFrom(mTempTouchState);
}
} else {
#if DEBUG_FOCUS
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a963c72..a865d9f 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1197,7 +1197,14 @@
switch (rawEvent->type) {
case EV_KEY:
switch (rawEvent->scanCode) {
- case BTN_MOUSE:
+ case BTN_LEFT:
+ case BTN_RIGHT:
+ case BTN_MIDDLE:
+ case BTN_SIDE:
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ case BTN_BACK:
+ case BTN_TASK:
mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = rawEvent->value != 0;
// Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
@@ -1247,6 +1254,7 @@
int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
+ float vscroll, hscroll;
{ // acquire lock
AutoMutex _l(mLock);
@@ -1331,10 +1339,14 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f);
if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, mAccumulator.relWheel);
+ vscroll = mAccumulator.relWheel;
+ } else {
+ vscroll = 0;
}
if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, mAccumulator.relHWheel);
+ hscroll = mAccumulator.relHWheel;
+ } else {
+ hscroll = 0;
}
} // release lock
@@ -1345,6 +1357,15 @@
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
mAccumulator.clear();
+
+ if (vscroll != 0 || hscroll != 0) {
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0,
+ AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
+ }
}
int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {