diff options
-rw-r--r-- | api/current.xml | 47 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 23 | ||||
-rw-r--r-- | core/java/android/app/Dialog.java | 23 | ||||
-rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 17 | ||||
-rw-r--r-- | core/java/android/view/MotionEvent.java | 60 | ||||
-rw-r--r-- | core/java/android/view/View.java | 124 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 52 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 52 | ||||
-rw-r--r-- | core/java/android/webkit/WebView.java | 28 | ||||
-rw-r--r-- | core/java/android/widget/AbsListView.java | 21 | ||||
-rw-r--r-- | core/java/android/widget/HorizontalScrollView.java | 35 | ||||
-rw-r--r-- | core/java/android/widget/ScrollView.java | 30 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/PointerLocationView.java | 123 | ||||
-rw-r--r-- | native/include/android/input.h | 9 | ||||
-rwxr-xr-x | policy/src/com/android/internal/policy/impl/PhoneWindowManager.java | 2 | ||||
-rw-r--r-- | services/input/EventHub.cpp | 72 | ||||
-rw-r--r-- | services/input/EventHub.h | 1 | ||||
-rw-r--r-- | services/input/InputDispatcher.cpp | 30 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 27 |
19 files changed, 622 insertions, 154 deletions
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 @@ 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() & 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() & 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() & 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 @@ 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); 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 @@ 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 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 @@ 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 b31381a..25db25e 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 23bb344..f7936d2 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 2e3f0bd..c064a9c 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 a963c72..a865d9f 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) { |