Processing Input Events
Processing Input Events
Android automatically provides a basic level of support for GamePads for many applications, however dedicated code is required to properly support all GamePad features. This technical note details how to write controller-aware software for Android and the PlayJam platform.
Compatible ControllersCompatible Controllers
The PlayJam Platform supports standard HID controllers, which are processed using Android standard mechanisms. The available inputs are as follows:
Name |
KeyCode (KeyEvent.*) or Motion Axis (MotionEvent.*) |
Fallback
|
Left Thumbstick |
MotionEvent.AXIS_X |
- |
Event SourcesEvent Sources
Note that the source identified returned by an event (via MotionEvent.getSource() or equivalent returns the source identifier for the device – which generally corresponds with the full set of capabilities of the device. As such, all events regardless of type will report as tge source type. IE – D-Pas keyboard event, Button Presses and Analog Motion will all report the same type – such as SOURCE_JOYSTICK.
Input Processing on AndroidInput Processing on Android
Android offer input events to applications in several passes. Analog dpad events may be rewritten as key events. Key events may be rewritten as fallback events if they’re not processes by the application. Navigation events may be intercepted by the application but failing that they are processed by Android – this is how Android provides basic navigational support for GamePads.
Note hat Home key events cannot be intercepted – they are processed directly by Android for launching the ‘Home’ app.
Multiple ControllersMultiple Controllers
Android Bugs Android Bugs
Due to an overly simplistic implementation of the dpad translation code in Android, (up to and including Android 4.4), using onKeyDown() and onKeyUp() events with multiple controllers is not recommended. While onKey*() events work acceptably for single controllers, when multiple controllers are used it is possible for events from one controller to be misidentified as coming from another. Details of this bug are included in Appendix A.
Identifying ControllersIdentifying Controllers
Android provides several different methods for identifying controllers, however they are not all equal in terms of functionality:
- InputDevice.getId()
- Guaranteed unique for the current power cycle
- Not stable – They are simple incrementing integers that are assigned to each device as it connects – even if it is a device that has already been connected – as such it is guaranteed that a different value will be returned for a controller before and after a power cycle of that controller, (i.e. if it disconnects, its getId() will change).
- InputDevice.getDescriptor()
- Stable – The value for any controllers will stay the same through power cycles of the controller and/or the platform – the value itself is derived from physical properties of the device.
- Not guaranteed unique – As the value is derived from a set of physical parameters of the device, it is possible for devices which share the same set of parameters to return the same value. This is generally not a problem for Bluetooth devices, as the MAC address of the Bluetooth client is part of the input to the id generator.
- InputDevice.getName()
- PlayJam provides managed naming of controllers – in that the controller with a specific player lit up will remain the same name – so the controller with light one on, (Player 1), will always return the device name of “GameStick Controller 1”. Note that the order of light assignment is currently related to the power up order of controllers – so turning off controllers and repowering in a different order will result in name changes – however the controller itself will still show the correct light for its name, (making the assignment clean to the user).
Example CodeExample Code
private static final float DPAD_DEAD_ZONE = 0.5f; @Override
public boolean onGenericMotionEvent(MotionEvent event) {
{ } { } {
float x = event.getAxisValue(MotionEvent.AXIS_Z);
float x = event.getAxisValue(MotionEvent.AXIS_HAT_X);
int intX = (int) ((Math.abs(x)< DPAD_DEAD_ZONE)? 0 : Math.signum(x));
int dpadXState = mDpadXState.get(padId);
// Send up events for keys that were down
// Send down events for the key that's pressed mDpadXState.put(padId, intX); } int dpadYState = mDpadYState.get(padId); if (dpadYState != intY) { // Send up events for keys that were down
if (dpadYState > 0) {
// Send down events for the key that's pressed mDpadYState.put(padId, intY); } } return true ; }
public void doKeyEvent(MotionEvent event, int action, int keycode) {
if (action == KeyEvent.ACTION_DOWN) {
public void onLeftStickEvent(int padId, int x, int y) {
public void onRightStickEvent(int padId, int x, int y){ |
APPENDIX A – ANDROID MULTIPLE D-PAD BUGAPPENDIX A – ANDROID MULTIPLE D-PAD BUG
ViewRootImpl.java/deliverGenericMotionEvent(...)
3337 |
ViewRootImpl.java/updateJoystickDirection(...)
3356private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { |