Add joystick support to framework.
Change-Id: I95374436708752e1a9cff3f85c5b9bc3e0987961
diff --git a/api/current.xml b/api/current.xml
index b733d89..7b19677 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -22816,6 +22816,19 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ev" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="dispatchKeyEvent"
return="boolean"
abstract="false"
@@ -23646,6 +23659,19 @@
visibility="public"
>
</method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -27595,6 +27621,19 @@
visibility="public"
>
</method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ev" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="dispatchKeyEvent"
return="boolean"
abstract="false"
@@ -27950,6 +27989,19 @@
visibility="public"
>
</method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -207180,6 +207232,17 @@
visibility="public"
>
</field>
+<field name="SOURCE_CLASS_JOYSTICK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SOURCE_CLASS_MASK"
type="int"
transient="false"
@@ -207235,6 +207298,28 @@
visibility="public"
>
</field>
+<field name="SOURCE_GAMEPAD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1025"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SOURCE_JOYSTICK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16777232"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SOURCE_KEYBOARD"
type="int"
transient="false"
@@ -209077,6 +209162,182 @@
visibility="public"
>
</field>
+<field name="KEYCODE_BUTTON_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="188"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_10"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="197"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_11"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="198"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="199"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_13"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="200"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_14"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="201"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_15"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="202"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_16"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="203"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="189"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="190"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="191"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="193"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="194"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="195"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="196"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="KEYCODE_BUTTON_A"
type="int"
transient="false"
@@ -216194,6 +216455,19 @@
<parameter name="canvas" type="android.graphics.Canvas">
</parameter>
</method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="dispatchKeyEvent"
return="boolean"
abstract="false"
@@ -218336,6 +218610,19 @@
<parameter name="previouslyFocusedRect" type="android.graphics.Rect">
</parameter>
</method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -225273,6 +225560,19 @@
<parameter name="hardwareAccelerated" type="boolean">
</parameter>
</method>
+<method name="superDispatchGenericMotionEvent"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="superDispatchKeyEvent"
return="boolean"
abstract="true"
@@ -225618,6 +225918,19 @@
deprecated="not deprecated"
visibility="public"
>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="dispatchKeyEvent"
return="boolean"
abstract="true"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6f0cb45..3eef785 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2081,7 +2081,39 @@
public boolean onTrackballEvent(MotionEvent event) {
return false;
}
-
+
+ /**
+ * 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
+ * {@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>
+ * <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.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
/**
* Called whenever a key, touch, or trackball event is dispatched to the
* activity. Implement this method if you wish to know that the user has
@@ -2264,6 +2296,24 @@
return onTrackballEvent(ev);
}
+ /**
+ * Called to process generic motion events. You can override this to
+ * intercept all generic motion events before they are dispatched to the
+ * window. Be sure to call this implementation for generic motion events
+ * that should be handled normally.
+ *
+ * @param ev The generic motion event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ onUserInteraction();
+ if (getWindow().superDispatchGenericMotionEvent(ev)) {
+ return true;
+ }
+ return onGenericMotionEvent(ev);
+ }
+
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
event.setClassName(getClass().getName());
event.setPackageName(getPackageName());
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6791400..b0929dd 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -633,7 +633,39 @@
public boolean onTrackballEvent(MotionEvent event) {
return false;
}
-
+
+ /**
+ * 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
+ * {@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>
+ * <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.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
if (mDecor != null) {
mWindowManager.updateViewLayout(mDecor, params);
@@ -722,6 +754,23 @@
return onTrackballEvent(ev);
}
+ /**
+ * Called to process generic motion events. You can override this to
+ * intercept all generic motion events before they are dispatched to the
+ * window. Be sure to call this implementation for generic motion events
+ * that should be handled normally.
+ *
+ * @param ev The generic motion event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ if (mWindow.superDispatchGenericMotionEvent(ev)) {
+ return true;
+ }
+ return onGenericMotionEvent(ev);
+ }
+
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
event.setClassName(getClass().getName());
event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index dd04975..e799f76 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -98,7 +98,16 @@
* Use {@link #getMotionRange} to query the range of positions.
*/
public static final int SOURCE_CLASS_POSITION = 0x00000008;
-
+
+ /**
+ * The input source is a joystick.
+ *
+ * A {@link MotionEvent} should be interpreted as absolute joystick movements.
+ *
+ * Use {@link #getMotionRange} to query the range of positions.
+ */
+ public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+
/**
* The input source is unknown.
*/
@@ -117,7 +126,15 @@
* @see #SOURCE_CLASS_BUTTON
*/
public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
-
+
+ /**
+ * The input source is a game pad.
+ * (It may also be a {@link #SOURCE_JOYSTICK}).
+ *
+ * @see #SOURCE_CLASS_BUTTON
+ */
+ public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
+
/**
* The input source is a touch screen pointing device.
*
@@ -148,7 +165,15 @@
* @see #SOURCE_CLASS_POSITION
*/
public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
-
+
+ /**
+ * The input source is a joystick.
+ * (It may also be a {@link #SOURCE_GAMEPAD}).
+ *
+ * @see #SOURCE_CLASS_JOYSTICK
+ */
+ public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
+
/**
* A special input source constant that is used when filtering input devices
* to match devices that provide any type of input source.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ecf1aef..695d16a 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -533,8 +533,40 @@
/** Key code constant: App switch key.
* Should bring up the application switcher dialog. */
public static final int KEYCODE_APP_SWITCH = 187;
+ /** Key code constant: Generic Game Pad Button #1.*/
+ public static final int KEYCODE_BUTTON_1 = 188;
+ /** Key code constant: Generic Game Pad Button #2.*/
+ public static final int KEYCODE_BUTTON_2 = 189;
+ /** Key code constant: Generic Game Pad Button #3.*/
+ public static final int KEYCODE_BUTTON_3 = 190;
+ /** Key code constant: Generic Game Pad Button #4.*/
+ public static final int KEYCODE_BUTTON_4 = 191;
+ /** Key code constant: Generic Game Pad Button #5.*/
+ public static final int KEYCODE_BUTTON_5 = 192;
+ /** Key code constant: Generic Game Pad Button #6.*/
+ public static final int KEYCODE_BUTTON_6 = 193;
+ /** Key code constant: Generic Game Pad Button #7.*/
+ public static final int KEYCODE_BUTTON_7 = 194;
+ /** Key code constant: Generic Game Pad Button #8.*/
+ public static final int KEYCODE_BUTTON_8 = 195;
+ /** Key code constant: Generic Game Pad Button #9.*/
+ public static final int KEYCODE_BUTTON_9 = 196;
+ /** Key code constant: Generic Game Pad Button #10.*/
+ public static final int KEYCODE_BUTTON_10 = 197;
+ /** Key code constant: Generic Game Pad Button #11.*/
+ public static final int KEYCODE_BUTTON_11 = 198;
+ /** Key code constant: Generic Game Pad Button #12.*/
+ public static final int KEYCODE_BUTTON_12 = 199;
+ /** Key code constant: Generic Game Pad Button #13.*/
+ public static final int KEYCODE_BUTTON_13 = 200;
+ /** Key code constant: Generic Game Pad Button #14.*/
+ public static final int KEYCODE_BUTTON_14 = 201;
+ /** Key code constant: Generic Game Pad Button #15.*/
+ public static final int KEYCODE_BUTTON_15 = 202;
+ /** Key code constant: Generic Game Pad Button #16.*/
+ public static final int KEYCODE_BUTTON_16 = 203;
- private static final int LAST_KEYCODE = KEYCODE_APP_SWITCH;
+ private static final int LAST_KEYCODE = KEYCODE_BUTTON_16;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
@@ -741,6 +773,22 @@
"KEYCODE_PROG_YELLOW",
"KEYCODE_PROG_BLUE",
"KEYCODE_APP_SWITCH",
+ "KEYCODE_BUTTON_1",
+ "KEYCODE_BUTTON_2",
+ "KEYCODE_BUTTON_3",
+ "KEYCODE_BUTTON_4",
+ "KEYCODE_BUTTON_5",
+ "KEYCODE_BUTTON_6",
+ "KEYCODE_BUTTON_7",
+ "KEYCODE_BUTTON_8",
+ "KEYCODE_BUTTON_9",
+ "KEYCODE_BUTTON_10",
+ "KEYCODE_BUTTON_11",
+ "KEYCODE_BUTTON_12",
+ "KEYCODE_BUTTON_13",
+ "KEYCODE_BUTTON_14",
+ "KEYCODE_BUTTON_15",
+ "KEYCODE_BUTTON_16",
};
// Symbolic names of all metakeys in bit order from least significant to most significant.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index db2cd50..0a5c5c6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -26,7 +26,8 @@
* class may hold either absolute or relative movements, depending on what
* it is being used for.
* <p>
- * On pointing devices such as touch screens, pointer coordinates specify absolute
+ * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER}
+ * such as touch screens, the pointer coordinates specify absolute
* positions such as view X/Y coordinates. Each complete gesture is represented
* by a sequence of motion events with actions that describe pointer state transitions
* and movements. A gesture starts with a motion event with {@link #ACTION_DOWN}
@@ -38,11 +39,18 @@
* by a motion event with {@link #ACTION_UP} or when gesture is canceled
* with {@link #ACTION_CANCEL}.
* </p><p>
- * On trackballs, the pointer coordinates specify relative movements as X/Y deltas.
+ * 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
* events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN}
* or {@link #ACTION_UP} motion events when the trackball button is pressed or released.
* </p><p>
+ * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK},
+ * the pointer coordinates specify the absolute position of the joystick axes.
+ * 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}.
+ * </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,
* except when the gesture is canceled.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f05ef8c..57520d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4493,6 +4493,16 @@
}
/**
+ * Pass a generic motion event down to the focused view.
+ *
+ * @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) {
+ return onGenericMotionEvent(event);
+ }
+
+ /**
* Called when the window containing this view gains or loses window focus.
* ViewGroups should override to route to their children.
*
@@ -4994,6 +5004,37 @@
}
/**
+ * 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
+ * {@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>
+ * <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.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
* Implement this method to handle touch screen motion events.
*
* @param event The motion event.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f6b6778..dbc4779 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1124,6 +1124,19 @@
* {@inheritDoc}
*/
@Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+ return super.dispatchGenericMotionEvent(event);
+ } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+ return mFocused.dispatchGenericMotionEvent(event);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index ad9e6863..4ed9742 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -128,6 +128,11 @@
final TrackballAxis mTrackballAxisX = new TrackballAxis();
final TrackballAxis mTrackballAxisY = new TrackballAxis();
+ int mLastJoystickXDirection;
+ int mLastJoystickYDirection;
+ int mLastJoystickXKeyCode;
+ int mLastJoystickYKeyCode;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -1878,6 +1883,7 @@
public final static int CLOSE_SYSTEM_DIALOGS = 1014;
public final static int DISPATCH_DRAG_EVENT = 1015;
public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
+ public final static int DISPATCH_GENERIC_MOTION = 1017;
@Override
public void handleMessage(Message msg) {
@@ -1914,6 +1920,9 @@
case DISPATCH_TRACKBALL:
deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
break;
+ case DISPATCH_GENERIC_MOTION:
+ deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
@@ -2420,6 +2429,102 @@
}
}
+ private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+ final int source = event.getSource();
+ final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
+
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ if (isJoystick) {
+ updateJoystickDirection(event, false);
+ }
+ finishGenericMotionEvent(event, sendDone, false);
+ return;
+ }
+
+ // Deliver the event to the view.
+ if (mView.dispatchGenericMotionEvent(event)) {
+ ensureTouchMode(false);
+ if (isJoystick) {
+ updateJoystickDirection(event, false);
+ }
+ finishGenericMotionEvent(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);
+ } else {
+ finishGenericMotionEvent(event, sendDone, false);
+ }
+ }
+
+ private void finishGenericMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ event.recycle();
+ if (sendDone) {
+ finishInputEvent(handled);
+ }
+ }
+
+ private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+ final long time = event.getEventTime();
+ final int metaState = event.getMetaState();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ final int xDirection = joystickAxisValueToDirection(event.getX());
+ final int yDirection = joystickAxisValueToDirection(event.getY());
+
+ if (xDirection != mLastJoystickXDirection) {
+ if (mLastJoystickXKeyCode != 0) {
+ deliverKeyEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ mLastJoystickXKeyCode = 0;
+ }
+
+ mLastJoystickXDirection = xDirection;
+
+ if (xDirection != 0 && synthesizeNewKeys) {
+ mLastJoystickXKeyCode = xDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+ deliverKeyEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ }
+ }
+
+ if (yDirection != mLastJoystickYDirection) {
+ if (mLastJoystickYKeyCode != 0) {
+ deliverKeyEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ mLastJoystickYKeyCode = 0;
+ }
+
+ mLastJoystickYDirection = yDirection;
+
+ if (yDirection != 0 && synthesizeNewKeys) {
+ mLastJoystickYKeyCode = yDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ deliverKeyEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ }
+ }
+ }
+
+ private static int joystickAxisValueToDirection(float value) {
+ if (value >= 0.5f) {
+ return 1;
+ } else if (value <= -0.5f) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
/**
* Returns true if the key is used for keyboard navigation.
* @param keyEvent The key event.
@@ -3053,11 +3158,7 @@
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
dispatchTrackball(event, sendDone);
} else {
- // TODO
- Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
- if (sendDone) {
- finishInputEvent(false);
- }
+ dispatchGenericMotion(event, sendDone);
}
}
@@ -3082,7 +3183,14 @@
msg.arg1 = sendDone ? 1 : 0;
sendMessageAtTime(msg, event.getEventTime());
}
-
+
+ private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
+ Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
+ msg.obj = event;
+ msg.arg1 = sendDone ? 1 : 0;
+ sendMessageAtTime(msg, event.getEventTime());
+ }
+
public void dispatchAppVisibility(boolean visible) {
Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
msg.arg1 = visible ? 1 : 0;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2f27935..5cbaf2a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -185,6 +185,18 @@
public boolean dispatchTrackballEvent(MotionEvent event);
/**
+ * Called to process generic motion events. At the very least your
+ * implementation must call
+ * {@link android.view.Window#superDispatchGenericMotionEvent} to do the
+ * standard processing.
+ *
+ * @param event The generic motion event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchGenericMotionEvent(MotionEvent event);
+
+ /**
* Called to process population of {@link AccessibilityEvent}s.
*
* @param event The event.
@@ -1063,6 +1075,14 @@
public abstract boolean superDispatchTrackballEvent(MotionEvent event);
/**
+ * Used by custom windows, such as Dialog, to pass the generic motion event
+ * further down the view hierarchy. Application developers should
+ * not need to implement or call this.
+ *
+ */
+ public abstract boolean superDispatchGenericMotionEvent(MotionEvent event);
+
+ /**
* Retrieve the top-level window decor view (containing the standard
* window frame/decorations and the client's content inside of that), which
* can be added as a window to the window manager.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 19e2b8d..3f02b0b 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1310,6 +1310,22 @@
<enum name="KEYCODE_PROG_YELLOW" value="185" />
<enum name="KEYCODE_PROG_BLUE" value="186" />
<enum name="KEYCODE_APP_SWITCH" value="187" />
+ <enum name="KEYCODE_BUTTON_1" value="188" />
+ <enum name="KEYCODE_BUTTON_2" value="189" />
+ <enum name="KEYCODE_BUTTON_3" value="190" />
+ <enum name="KEYCODE_BUTTON_4" value="191" />
+ <enum name="KEYCODE_BUTTON_5" value="192" />
+ <enum name="KEYCODE_BUTTON_6" value="193" />
+ <enum name="KEYCODE_BUTTON_7" value="194" />
+ <enum name="KEYCODE_BUTTON_8" value="195" />
+ <enum name="KEYCODE_BUTTON_9" value="196" />
+ <enum name="KEYCODE_BUTTON_10" value="197" />
+ <enum name="KEYCODE_BUTTON_11" value="198" />
+ <enum name="KEYCODE_BUTTON_12" value="199" />
+ <enum name="KEYCODE_BUTTON_13" value="200" />
+ <enum name="KEYCODE_BUTTON_14" value="201" />
+ <enum name="KEYCODE_BUTTON_15" value="202" />
+ <enum name="KEYCODE_BUTTON_16" value="203" />
</attr>
<!-- ***************************************************************** -->
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 1ee121e..0aefc31 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -262,6 +262,24 @@
# key 240 "KEY_UNKNOWN"
+key 288 BUTTON_1
+key 289 BUTTON_2
+key 290 BUTTON_3
+key 291 BUTTON_4
+key 292 BUTTON_5
+key 293 BUTTON_6
+key 294 BUTTON_7
+key 295 BUTTON_8
+key 296 BUTTON_9
+key 297 BUTTON_10
+key 298 BUTTON_11
+key 299 BUTTON_12
+key 300 BUTTON_13
+key 301 BUTTON_14
+key 302 BUTTON_15
+key 303 BUTTON_16
+
+
key 304 BUTTON_A
key 305 BUTTON_B
key 306 BUTTON_C
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index 9b1a897..dbccf29 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -212,6 +212,22 @@
{ "PROG_YELLOW", 185 },
{ "PROG_BLUE", 186 },
{ "APP_SWITCH", 187 },
+ { "BUTTON_1", 188 },
+ { "BUTTON_2", 189 },
+ { "BUTTON_3", 190 },
+ { "BUTTON_4", 191 },
+ { "BUTTON_5", 192 },
+ { "BUTTON_6", 193 },
+ { "BUTTON_7", 194 },
+ { "BUTTON_8", 195 },
+ { "BUTTON_9", 196 },
+ { "BUTTON_10", 197 },
+ { "BUTTON_11", 198 },
+ { "BUTTON_12", 199 },
+ { "BUTTON_13", 200 },
+ { "BUTTON_14", 201 },
+ { "BUTTON_15", 202 },
+ { "BUTTON_16", 203 },
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/native/include/android/input.h b/native/include/android/input.h
index e196686..bad363d 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -333,6 +333,7 @@
AINPUT_SOURCE_CLASS_POINTER = 0x00000002,
AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004,
AINPUT_SOURCE_CLASS_POSITION = 0x00000008,
+ AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010,
};
enum {
@@ -340,10 +341,12 @@
AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON,
+ AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
+ AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
AINPUT_SOURCE_ANY = 0xffffff00,
};
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index b026a0c..c4a7eff 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -231,6 +231,22 @@
AKEYCODE_PROG_YELLOW = 185,
AKEYCODE_PROG_BLUE = 186,
AKEYCODE_APP_SWITCH = 187,
+ AKEYCODE_BUTTON_1 = 188,
+ AKEYCODE_BUTTON_2 = 189,
+ AKEYCODE_BUTTON_3 = 190,
+ AKEYCODE_BUTTON_4 = 191,
+ AKEYCODE_BUTTON_5 = 192,
+ AKEYCODE_BUTTON_6 = 193,
+ AKEYCODE_BUTTON_7 = 194,
+ AKEYCODE_BUTTON_8 = 195,
+ AKEYCODE_BUTTON_9 = 196,
+ AKEYCODE_BUTTON_10 = 197,
+ AKEYCODE_BUTTON_11 = 198,
+ AKEYCODE_BUTTON_12 = 199,
+ AKEYCODE_BUTTON_13 = 200,
+ AKEYCODE_BUTTON_14 = 201,
+ AKEYCODE_BUTTON_15 = 202,
+ AKEYCODE_BUTTON_16 = 203,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index d6b7366..8e670b8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1278,6 +1278,11 @@
return mDecor.superDispatchTrackballEvent(event);
}
+ @Override
+ public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+ return mDecor.superDispatchGenericMotionEvent(event);
+ }
+
/**
* A key was pressed down and not handled by anything else in the window.
*
@@ -1691,6 +1696,13 @@
.dispatchTrackballEvent(ev);
}
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ final Callback cb = getCallback();
+ return cb != null && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super
+ .dispatchGenericMotionEvent(ev);
+ }
+
public boolean superDispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
@@ -1707,6 +1719,10 @@
return super.dispatchTrackballEvent(event);
}
+ public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+ return super.dispatchGenericMotionEvent(event);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
return onInterceptTouchEvent(event);
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 487e73f..efdb177 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -624,7 +624,11 @@
AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
- AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
+ AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4,
+ AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8,
+ AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12,
+ AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16,
};
int EventHub::openDevice(const char *devicePath) {
@@ -739,9 +743,9 @@
//}
// See if this is a keyboard. Ignore everything in the button range except for
- // gamepads which are also considered keyboards.
+ // joystick and gamepad buttons which are also considered keyboards.
if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
- || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI))
|| containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1))) {
@@ -856,6 +860,18 @@
}
}
+ // See if this device is a joystick.
+ // Ignore touchscreens because they use the same absolute axes for other purposes.
+ if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD
+ && !(device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) {
+ if (test_bit(ABS_X, abs_bitmask)
+ || test_bit(ABS_Y, abs_bitmask)
+ || test_bit(ABS_HAT0X, abs_bitmask)
+ || test_bit(ABS_HAT0Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_JOYSTICK;
+ }
+ }
+
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
LOGV("Dropping device: id=%d, path='%s', name='%s'",
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 74b7ec5..3b5a1ea 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -85,7 +85,7 @@
int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
- inline int32_t getRange() { return maxValue - minValue; }
+ inline int32_t getRange() const { return maxValue - minValue; }
inline void clear() {
valid = false;
@@ -100,7 +100,7 @@
* Input device classes.
*/
enum {
- /* The input device is a keyboard. */
+ /* The input device is a keyboard or has buttons. */
INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
@@ -123,6 +123,9 @@
/* The input device has switches. */
INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+
+ /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
+ INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
};
/*
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 6b66791..77b745f 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -250,6 +250,9 @@
if (classes & INPUT_DEVICE_CLASS_DPAD) {
keyboardSources |= AINPUT_SOURCE_DPAD;
}
+ if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+ }
if (keyboardSources != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
@@ -267,6 +270,11 @@
device->addMapper(new SingleTouchInputMapper(device));
}
+ // Joystick-like devices.
+ if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+ device->addMapper(new JoystickInputMapper(device));
+ }
+
return device;
}
@@ -715,6 +723,16 @@
return 0;
}
+void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
+ const RawAbsoluteAxisInfo& axis, const char* name) {
+ if (axis.valid) {
+ dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
+ name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+ } else {
+ dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+ }
+}
+
// --- SwitchInputMapper ---
@@ -857,7 +875,7 @@
bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
return scanCode < BTN_MOUSE
|| scanCode >= KEY_OK
- || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+ || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
}
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
@@ -1486,25 +1504,16 @@
mRawAxes.orientation.clear();
}
-static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
- if (axis.valid) {
- dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
- name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
- } else {
- dump.appendFormat(INDENT4 "%s: unknown range\n", name);
- }
-}
-
void TouchInputMapper::dumpRawAxes(String8& dump) {
dump.append(INDENT3 "Raw Axes:\n");
- dumpAxisInfo(dump, mRawAxes.x, "X");
- dumpAxisInfo(dump, mRawAxes.y, "Y");
- dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
- dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
- dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
- dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
- dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
- dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation");
}
bool TouchInputMapper::configureSurfaceLocked() {
@@ -3646,4 +3655,199 @@
}
+// --- JoystickInputMapper ---
+
+JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
+ InputMapper(device) {
+ initialize();
+}
+
+JoystickInputMapper::~JoystickInputMapper() {
+}
+
+uint32_t JoystickInputMapper::getSources() {
+ return AINPUT_SOURCE_JOYSTICK;
+}
+
+void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ if (mAxes.x.valid) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_X,
+ mAxes.x.min, mAxes.x.max, mAxes.x.flat, mAxes.x.fuzz);
+ }
+ if (mAxes.y.valid) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y,
+ mAxes.y.min, mAxes.y.max, mAxes.y.flat, mAxes.y.fuzz);
+ }
+}
+
+void JoystickInputMapper::dump(String8& dump) {
+ dump.append(INDENT2 "Joystick Input Mapper:\n");
+
+ dump.append(INDENT3 "Raw Axes:\n");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
+ dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
+
+ dump.append(INDENT3 "Normalized Axes:\n");
+ dumpNormalizedAxis(dump, mAxes.x, "X");
+ dumpNormalizedAxis(dump, mAxes.y, "Y");
+ dumpNormalizedAxis(dump, mAxes.hat0X, "Hat0X");
+ dumpNormalizedAxis(dump, mAxes.hat0Y, "Hat0Y");
+}
+
+void JoystickInputMapper::dumpNormalizedAxis(String8& dump,
+ const NormalizedAxis& axis, const char* name) {
+ if (axis.valid) {
+ dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, "
+ "scale=%0.3f, center=%0.3f, precision=%0.3f, value=%0.3f\n",
+ name, axis.min, axis.max, axis.flat, axis.fuzz,
+ axis.scale, axis.center, axis.precision, axis.value);
+ } else {
+ dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+ }
+}
+
+void JoystickInputMapper::configure() {
+ InputMapper::configure();
+
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0X, & mRawAxes.hat0X);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0Y, & mRawAxes.hat0Y);
+
+ mAxes.x.configure(mRawAxes.x);
+ mAxes.y.configure(mRawAxes.y);
+ mAxes.hat0X.configure(mRawAxes.hat0X);
+ mAxes.hat0Y.configure(mRawAxes.hat0Y);
+}
+
+void JoystickInputMapper::initialize() {
+ mAccumulator.clear();
+
+ mAxes.x.resetState();
+ mAxes.y.resetState();
+ mAxes.hat0X.resetState();
+ mAxes.hat0Y.resetState();
+}
+
+void JoystickInputMapper::reset() {
+ // Recenter all axes.
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mAccumulator.clear();
+ mAccumulator.fields = Accumulator::FIELD_ALL;
+ sync(when);
+
+ // Reinitialize state.
+ initialize();
+
+ InputMapper::reset();
+}
+
+void JoystickInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS:
+ switch (rawEvent->scanCode) {
+ case ABS_X:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_X;
+ mAccumulator.absX = rawEvent->value;
+ break;
+ case ABS_Y:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
+ mAccumulator.absY = rawEvent->value;
+ break;
+ case ABS_HAT0X:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0X;
+ mAccumulator.absHat0X = rawEvent->value;
+ break;
+ case ABS_HAT0Y:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0Y;
+ mAccumulator.absHat0Y = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void JoystickInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
+ int32_t metaState = mContext->getGlobalMetaState();
+
+ bool motionAxisChanged = false;
+ if (fields & Accumulator::FIELD_ABS_X) {
+ if (mAxes.x.updateValue(mAccumulator.absX)) {
+ motionAxisChanged = true;
+ }
+ }
+
+ if (fields & Accumulator::FIELD_ABS_Y) {
+ if (mAxes.y.updateValue(mAccumulator.absY)) {
+ motionAxisChanged = true;
+ }
+ }
+
+ if (motionAxisChanged) {
+ PointerCoords pointerCoords;
+ pointerCoords.x = mAxes.x.value;
+ pointerCoords.y = mAxes.y.value;
+ pointerCoords.touchMajor = 0;
+ pointerCoords.touchMinor = 0;
+ pointerCoords.toolMajor = 0;
+ pointerCoords.toolMinor = 0;
+ pointerCoords.pressure = 0;
+ pointerCoords.size = 0;
+ pointerCoords.orientation = 0;
+
+ int32_t pointerId = 0;
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+ AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, &pointerId, &pointerCoords, mAxes.x.precision, mAxes.y.precision, 0);
+ }
+
+ if (fields & Accumulator::FIELD_ABS_HAT0X) {
+ if (mAxes.hat0X.updateValueAndDirection(mAccumulator.absHat0X)) {
+ notifyDirectionalAxis(mAxes.hat0X, when, metaState,
+ AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT);
+ }
+ }
+
+ if (fields & Accumulator::FIELD_ABS_HAT0Y) {
+ if (mAxes.hat0Y.updateValueAndDirection(mAccumulator.absHat0Y)) {
+ notifyDirectionalAxis(mAxes.hat0Y, when, metaState,
+ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN);
+ }
+ }
+
+ mAccumulator.clear();
+}
+
+void JoystickInputMapper::notifyDirectionalAxis(DirectionalAxis& axis,
+ nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode) {
+ if (axis.lastKeyCode) {
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+ AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM,
+ axis.lastKeyCode, 0, metaState, when);
+ axis.lastKeyCode = 0;
+ }
+ if (axis.direction) {
+ axis.lastKeyCode = axis.direction > 0 ? highKeyCode : lowKeyCode;
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+ AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM,
+ axis.lastKeyCode, 0, metaState, when);
+ }
+}
+
+
} // namespace android
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 8b2d40a..613ed18 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -339,6 +339,9 @@
protected:
InputDevice* mDevice;
InputReaderContext* mContext;
+
+ static void dumpRawAbsoluteAxisInfo(String8& dump,
+ const RawAbsoluteAxisInfo& axis, const char* name);
};
@@ -951,6 +954,139 @@
void sync(nsecs_t when);
};
+
+class JoystickInputMapper : public InputMapper {
+public:
+ JoystickInputMapper(InputDevice* device);
+ virtual ~JoystickInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(String8& dump);
+ virtual void configure();
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+private:
+ struct RawAxes {
+ RawAbsoluteAxisInfo x;
+ RawAbsoluteAxisInfo y;
+ RawAbsoluteAxisInfo hat0X;
+ RawAbsoluteAxisInfo hat0Y;
+ } mRawAxes;
+
+ struct NormalizedAxis {
+ bool valid;
+
+ static const float min = -1.0f;
+ static const float max = -1.0f;
+
+ float scale; // scale factor
+ float center; // center offset after scaling
+ float precision; // precision
+ float flat; // size of flat region
+ float fuzz; // error tolerance
+
+ float value; // most recent value
+
+ NormalizedAxis() : valid(false), scale(0), center(0), precision(0),
+ flat(0), fuzz(0), value(0) {
+ }
+
+ void configure(const RawAbsoluteAxisInfo& rawAxis) {
+ if (rawAxis.valid && rawAxis.getRange() != 0) {
+ valid = true;
+ scale = 2.0f / rawAxis.getRange();
+ precision = rawAxis.getRange();
+ flat = rawAxis.flat * scale;
+ fuzz = rawAxis.fuzz * scale;
+ center = float(rawAxis.minValue + rawAxis.maxValue) / rawAxis.getRange();
+ }
+ }
+
+ void resetState() {
+ value = 0;
+ }
+
+ bool updateValue(int32_t rawValue) {
+ float newValue = rawValue * scale - center;
+ if (value == newValue) {
+ return false;
+ }
+ value = newValue;
+ return true;
+ }
+ };
+
+ struct DirectionalAxis : NormalizedAxis {
+ int32_t direction; // most recent direction vector: value is one of -1, 0, 1.
+
+ int32_t lastKeyCode; // most recent key code produced
+
+ DirectionalAxis() : lastKeyCode(0) {
+ }
+
+ void resetState() {
+ NormalizedAxis::resetState();
+ direction = 0;
+ lastKeyCode = 0;
+ }
+
+ bool updateValueAndDirection(int32_t rawValue) {
+ if (!updateValue(rawValue)) {
+ return false;
+ }
+ if (value > flat) {
+ direction = 1;
+ } else if (value < -flat) {
+ direction = -1;
+ } else {
+ direction = 0;
+ }
+ return true;
+ }
+ };
+
+ struct Axes {
+ NormalizedAxis x;
+ NormalizedAxis y;
+ DirectionalAxis hat0X;
+ DirectionalAxis hat0Y;
+ } mAxes;
+
+ struct Accumulator {
+ enum {
+ FIELD_ABS_X = 1,
+ FIELD_ABS_Y = 2,
+ FIELD_ABS_HAT0X = 4,
+ FIELD_ABS_HAT0Y = 8,
+
+ FIELD_ALL = FIELD_ABS_X | FIELD_ABS_Y | FIELD_ABS_HAT0X | FIELD_ABS_HAT0Y,
+ };
+
+ uint32_t fields;
+
+ int32_t absX;
+ int32_t absY;
+ int32_t absHat0X;
+ int32_t absHat0Y;
+
+ inline void clear() {
+ fields = 0;
+ }
+ } mAccumulator;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+
+ void notifyDirectionalAxis(DirectionalAxis& axis,
+ nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode);
+
+ static void dumpNormalizedAxis(String8& dump,
+ const NormalizedAxis& axis, const char* name);
+};
+
} // namespace android
#endif // _UI_INPUT_READER_H