Android API Specifics


API Levels

The above document tries to give advice for the broadest range of API Levels. If you’d like to use some newer functions, see InputDevice.getDescriptor (good for when the controller is disconnected or reconnected, API16), InputDevice.getVibrator (query vibrate capabilities, API16), InputManager class (good for knowing device states, API16).

Handling Controller Flats/Deadzones

Android gives the ability for a developer to ask for a controller’s “deadzone,” or flat per axis. This is the area around an axis in which there should be considered no movement, for instance, around the analog sticks. To get this area, call InputDevice.MotionRange.getFlat() which will return a float. Use this value as +/- around the axis. For instance, an axis with a flat value of 0.12f that goes from -1.0f to 1.0f should not consider movement from -0.12f to 0.12f. Consider having a minimum value of approximately 0.10f to take non-reporting into account.

Unless an application requires enlarging the flat range to give a certain feel to a controller, it should be avoided, and the value from getFlat should be used. Otherwise, applications typically feel sluggish and inputs latent.

MotionEvents

Every MotionEvent contains every axis for a particular source (see Filtering Events below) on a device, even if only one axis was changed. Many applications don’t care about every axis. For instance, an application may only use the left analog stick (AXIS_X/AXIS_Y) for movement, and nothing else. This application will still get an event if, for instance, the right analog stick (AXIS_Z/AXIS_RZ) was moved. If an application handles any MotionEvent, it is highly recommended that it indicates to Android it has handled the event by returning true. This will make sure Android will not perform its default action for an axis, which may not be what the user or developer expects.

MotionRanges

Consider that some controllers may have other functionality. This is usually mouse emulation. It is acceptable, and usual, for a controller to report the mouse axes as AXIS_X/AXIS_Y, just like the left analog stick. The difference between the two X/Y sets is that they come from different sources, which can be obtained using InputDevice.MotionRange.getSource(). This is a nuance that is easily missed. If there is a need to crawl through all axes at the InputDevice level, a developer should take this into account. Below is a sample where only controller axis ids are filtered.

for (MotionRange range : device.getMotionRanges())
{
if (((range.getSource() & InputDevice.SOURCE_GAMEPAD) != 0) ||
((range.getSource() & InputDevice.SOURCE_JOYSTICK) != 0))
{
int nAxisId = range.getAxis();
cacheAxisIds(device.getId(), nAxisId);
}
}

Most developers only care about the source at the MotionEvent level and not at the InputDevice. Understanding that axis ids from all sources can be obtained at the device level is an important input concept for a developer to know. This can allow more flexibility in controller, event, or input device handling algorithms.

Handling Multiple Controllers and Disconnects

Every attached input device, including controllers, has an associated device ID number. Using this ID number allows you to easily identify what attached device generated a particular event. This can be obtained by using the method InputEvent.getDeviceId(). Keeping track of IDs can allow you to develop single device multiplayer games.

Note that if a controller gets disconnected from the device and reconnects, it will lose its current device ID and receive a new one, this is especially important to be aware of in multi-controller games. API16 has a way to listen for device connects and disconnects; however, since API16 isn’t widely adopted yet, we need to consider other solutions. There are several ways of handling disconnect situations without API16 functionality.

One example would be to periodically poll the InputDevice.getDeviceIds(), using a Runnable that would run every 3 seconds or so, and wait for another controller to connect. Another way would be to wait for an input event that is not from the device ID(s) you expect, and ask the user about the new controller. See the API Levels topic above for further reading on recent APIs that could be helpful.

One more adventurous method would be to add the proper listeners for general USB disconnects, as well as Bluetooth disconnects. This is beyond the scope of this document and is just a point of knowledge. The complexity of implementation outweighs the need, given other solutions, and possible user experience.

Filtering Events

A device can generate input events from many sources: touchscreen, controllers, headsets, pens, etc. When it comes to controllers, there are a few ways a developer could filter events to the ones that matter. Both multiplayer and single player games should consider the first method described below. For a single player game, only the first method should be considered; otherwise, you risk unnecessary filtering. In short, filter only as needed.

Every event comes from one or more sources. To get an event’s source bits, use the method InputEvent.getSource(). For controllers, you should mask out the source class bits ~InputDevice.SOURCE_CLASS_MASK and test against the sources InputDevice.SOURCE_GAMEPAD and InputDevice.SOURCE_JOYSTICK. If the event is from one of those sources, it should be considered as coming from a controller. It should be noted, system keys such as (but not limited to) KEYCODE_BACK, KEYCODE_MENU, or KEYCODE_VOLUME_UP/DOWN, may or may not come from the controller, and therefore should not be filtered if needed.

Another way events could be filtered is by using the controller’s device ID. This is described in the Handling Multiple Controllers section above. Filtering by device ID is typically only needed when you are handling multiplayer single device situations, such as when you need to know which controller a particular event came from. In single player games, this type of filtering is usually unnecessary since it could overly restrict a user if not handled properly. Take the instance of a user first playing a game on the SHIELD device, connecting it to HDMI to play on the big screen, grabbing their already-paired Bluetooth controller, and sitting back on the couch. This scenario couldn’t happen if the game filtered strictly against the SHIELD controller’s ID. However, if the game did no device ID filtering at all, this scenario could easily work, and with less developer effort.

The final filtering method discussed here is to filter on the controller’s name. To get the controller’s name string use the method InputDevice.getName(). See Specific Controller Tuning section for more information. We would not recommend using this as a primary filtering method, as it can be very fragile. It should only be used in cases where you are tuning for a very specific controller. On the topic of fragility, in Android version 4.2.x, Bluetooth controllers do not report their name properly. They look to be reporting a base chip name, most commonly, “Broadcom Bluetooth HID.”

 


NVIDIA® GameWorks™ Documentation Rev. 1.0.200605 ©2014-2020. NVIDIA Corporation. All Rights Reserved.