summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Brown <jeffbrown@google.com> 2011-02-24 20:55:35 -0800
committer Jeff Brown <jeffbrown@google.com> 2011-02-25 17:06:07 -0800
commit33bbfd2232ea9eaae9a9d87a05a95a430f09bd83 (patch)
tree88e8e2513676dccf07b9764522a681aae370f020
parentd752c3b3e6c576ed1f18e86a7b18c33dc7c65791 (diff)
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
-rw-r--r--api/current.xml47
-rw-r--r--core/java/android/app/Activity.java23
-rw-r--r--core/java/android/app/Dialog.java23
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java17
-rw-r--r--core/java/android/view/MotionEvent.java60
-rw-r--r--core/java/android/view/View.java124
-rw-r--r--core/java/android/view/ViewGroup.java52
-rw-r--r--core/java/android/view/ViewRoot.java52
-rw-r--r--core/java/android/webkit/WebView.java28
-rw-r--r--core/java/android/widget/AbsListView.java21
-rw-r--r--core/java/android/widget/HorizontalScrollView.java35
-rw-r--r--core/java/android/widget/ScrollView.java30
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java123
-rw-r--r--native/include/android/input.h9
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java2
-rw-r--r--services/input/EventHub.cpp72
-rw-r--r--services/input/EventHub.h1
-rw-r--r--services/input/InputDispatcher.cpp30
-rw-r--r--services/input/InputReader.cpp27
19 files changed, 622 insertions, 154 deletions
diff --git a/api/current.xml b/api/current.xml
index 872e627d7f79..ad95c940fb18 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 67e480603f72..9e72c1b39732 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2122,24 +2122,21 @@ public class Activity extends ContextThemeWrapper
* 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() &amp; 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 cc4fefc3ea3b..087753b21987 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -627,24 +627,21 @@ public class Dialog implements DialogInterface, Window.Callback,
* 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() &amp; 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 dd3971402959..20661d76f741 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -460,16 +460,17 @@ public abstract class WallpaperService extends Service {
}
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 cc37a28f62cd..a26dd0425b69 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -40,6 +40,12 @@ import android.util.SparseArray;
* 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 @@ import android.util.SparseArray;
* 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 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* 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 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* <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 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
+ * 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 @@ public final class MotionEvent extends InputEvent implements Parcelable {
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 78eb2e5bee64..01bc2df2261a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -57,6 +57,7 @@ import android.util.Poolable;
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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private OnTouchListener mOnTouchListener;
+ private OnGenericMotionListener mOnGenericMotionListener;
+
private OnDragListener mOnDragListener;
private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
@@ -2257,6 +2260,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* 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() &amp; 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() &amp; 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * 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 a0d4263e58ff..5c06151c0dbc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1145,6 +1145,53 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
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 39f99b8c8412..c7b19555716c 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2247,7 +2247,7 @@ public final class ViewRoot extends Handler implements ViewParent,
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 @@ public final class ViewRoot extends Handler implements ViewParent,
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 @@ public final class ViewRoot extends Handler implements ViewParent,
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 @@ public final class ViewRoot extends Handler implements ViewParent,
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 @@ public final class ViewRoot extends Handler implements ViewParent,
// 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 @@ public final class ViewRoot extends Handler implements ViewParent,
// touch mode here.
ensureTouchMode(false);
- finishTrackballEvent(event, sendDone, true);
+ finishMotionEvent(event, sendDone, true);
mLastTrackballTime = Integer.MIN_VALUE;
return;
}
@@ -2471,14 +2475,7 @@ public final class ViewRoot extends Handler implements ViewParent,
// 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 @@ public final class ViewRoot extends Handler implements ViewParent,
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishGenericMotionEvent(event, sendDone, false);
+ finishMotionEvent(event, sendDone, false);
return;
}
@@ -2499,23 +2496,16 @@ public final class ViewRoot extends Handler implements ViewParent,
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 06cb64e81d08..98fc29022a93 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -62,6 +62,7 @@ import android.util.EventLog;
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 @@ public class WebView extends AbsoluteLayout
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 eb53e56b4b0d..c2c8d16a928a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -43,6 +43,7 @@ import android.view.ActionMode;
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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@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 e26e99b0213d..3255f6f35e38 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -26,6 +26,7 @@ import android.graphics.Rect;
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 @@ public class HorizontalScrollView extends FrameLayout {
}
@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 9932320b800f..7aca0dbb4ae9 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable;
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 @@ public class ScrollView extends FrameLayout {
}
@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 c72d0e82c8bc..5ac903d14985 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -318,10 +318,56 @@ public class PointerLocationView extends View {
}
}
- 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 @@ public class PointerLocationView extends View {
.toString());
}
- public void addTouchEvent(MotionEvent event) {
+ public void addPointerEvent(MotionEvent event) {
synchronized (mPointers) {
int action = event.getAction();
@@ -363,10 +409,16 @@ public class PointerLocationView extends View {
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 @@ public class PointerLocationView extends View {
}
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 @@ public class PointerLocationView extends View {
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 @@ public class PointerLocationView extends View {
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 @@ public class PointerLocationView extends View {
@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 d5160378dbb0..f19e8bedd88d 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -288,6 +288,15 @@ enum {
* 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 f5f4c6e3feaa..75ef7629b076 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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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 b31381a3531e..25db25e6773e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -508,6 +508,7 @@ bool EventHub::getEvent(RawEvent* outEvent) {
}
// Grab the next input event.
+ bool deviceWasRemoved = false;
for (;;) {
// Consume buffered input events, if any.
if (mInputBufferIndex < mInputBufferCount) {
@@ -558,6 +559,10 @@ bool EventHub::getEvent(RawEvent* outEvent) {
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 @@ bool EventHub::getEvent(RawEvent* outEvent) {
}
}
+ // 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 @@ bool EventHub::getEvent(RawEvent* outEvent) {
}
#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 @@ bool EventHub::getEvent(RawEvent* outEvent) {
usleep(100000);
}
}
+
+ // Prepare to process all of the FDs we just polled.
+ mInputFdIndex = 0;
}
}
@@ -1008,35 +1021,40 @@ int EventHub::closeDevice(const char *devicePath) {
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);
+ return closeDeviceAtIndexLocked(i);
+ }
+ }
+ LOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+ return -1;
+}
- mFds.removeAt(i);
- mDevices.removeAt(i);
- device->close();
+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);
- device->next = mClosingDevices;
- mClosingDevices = device;
- return 0;
+ for (int j=0; j<EV_SW; j++) {
+ if (mSwitches[j] == device->id) {
+ mSwitches[j] = 0;
}
}
- LOGE("remove device: %s not found\n", devicePath);
- return -1;
+
+ 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) {
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 23bb34493471..f7936d255b08 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -261,6 +261,7 @@ private:
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 2e3f0bdc9ff7..c064a9ceb157 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -116,6 +116,7 @@ static bool isValidMotionAction(int32_t action, size_t pointerCount) {
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 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
// 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 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
&& (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 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
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 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
// 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 @@ Failed:
|| 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 @@ Failed:
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 @@ Failed:
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 a963c726a212..a865d9fb1711 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1197,7 +1197,14 @@ void CursorInputMapper::process(const RawEvent* rawEvent) {
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 @@ void CursorInputMapper::sync(nsecs_t when) {
int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
+ float vscroll, hscroll;
{ // acquire lock
AutoMutex _l(mLock);
@@ -1331,10 +1339,14 @@ void CursorInputMapper::sync(nsecs_t when) {
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 @@ void CursorInputMapper::sync(nsecs_t when) {
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) {