summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-02-24 20:55:35 -0800
committerJeff Brown <jeffbrown@google.com>2011-02-25 17:06:07 -0800
commit33bbfd2232ea9eaae9a9d87a05a95a430f09bd83 (patch)
tree88e8e2513676dccf07b9764522a681aae370f020 /core
parentd752c3b3e6c576ed1f18e86a7b18c33dc7c65791 (diff)
downloadframeworks_base-33bbfd2232ea9eaae9a9d87a05a95a430f09bd83.zip
frameworks_base-33bbfd2232ea9eaae9a9d87a05a95a430f09bd83.tar.gz
frameworks_base-33bbfd2232ea9eaae9a9d87a05a95a430f09bd83.tar.bz2
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
Diffstat (limited to 'core')
-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
12 files changed, 476 insertions, 112 deletions
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 @@ 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 cc4fefc..087753b 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 dd39714..20661d7 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 cc37a28..a26dd04 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 78eb2e5..01bc2df 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 a0d4263..5c06151 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 39f99b8..c7b1955 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 06cb64e..98fc290 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 eb53e56..c2c8d16 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 e26e99b..3255f6f 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 9932320..7aca0db 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 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 @@ 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);