1page.title=Handling Controller Actions
2trainingnavtop=true
3
4@jd:body
5
6<!-- This is the training bar -->
7<div id="tb-wrapper">
8<div id="tb">
9
10<h2>This lesson teaches you to</h2>
11<ol>
12  <li><a href="#input">Verify a Game Controller is Connected</a></li>
13  <li><a href="#button">Process Gamepad Button Presses</a>
14  </li>
15  <li><a href="#dpad">Process Directional Pad Input</a>
16  </li>
17  <li><a href="#joystick">Process Joystick Movements</a>
18  </li>
19</ol>
20
21<h2>Try it out</h2>
22<div class="download-box">
23  <a href="http://developer.android.com/shareables/training/ControllerSample.zip"
24class="button">Download the sample</a>
25  <p class="filename">ControllerSample.zip</p>
26</div>
27
28</div>
29</div>
30
31<p>At the system level, Android reports input event codes from game controllers
32as Android key codes and axis values. In your game, you can receive these codes
33and values and convert them to specific in-game actions.</p>
34
35<p>When players physically connect or wirelessly pair a game controller to
36their Android-powered devices, the system auto-detects the controller
37as an input device and starts reporting its input events. Your game can receive
38these input events by implementing the following callback methods in your active
39{@link android.app.Activity} or focused {@link android.view.View} (you should
40implement the callbacks for either the {@link android.app.Activity} or
41{@link android.view.View}, but not both): </p>
42<ul>
43<li>From {@link android.app.Activity}:
44    <ul>
45    <li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent)
46      dispatchGenericMotionEvent(android.view. MotionEvent)}
47        <p>Called to process generic motion events such as joystick movements.</p>
48    </li>
49    <li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent)
50      dispatchKeyEvent(android.view.KeyEvent)}
51        <p>Called to process key events such as a press or release of a
52          gamepad or D-pad button.</p>
53    </li>
54    </ul>
55</li>
56<li>From {@link android.view.View}:
57    <ul>
58    <li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
59onGenericMotionEvent(android.view.MotionEvent)}
60        <p>Called to process generic motion events such as joystick movements.</p>
61    </li>
62    <li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)}
63        <p>Called to process a press of a physical key such as a gamepad or
64          D-pad button.</p>
65    </li>
66    <li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)}
67        <p>Called to process a release of a physical key such as a gamepad or
68          D-pad button.</p>
69    </li>
70    </ul>
71</li>
72</ul>
73
74<p>The recommended approach is to capture the events from the
75  specific {@link android.view.View} object that the user interacts with.
76  Inspect the following objects provided by the callbacks to get information
77  about the type of input event received:</p>
78
79<dl>
80<dt>{@link android.view.KeyEvent}</dt>
81   <dd>An object that describes directional
82pad</a> (D-pad) and gamepad button events. Key events are accompanied by a
83<em>key code</em> that indicates the specific button triggered, such as
84{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}
85or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the
86key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key
87event callbacks such as
88{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}.
89<dd>
90<dt>{@link android.view.MotionEvent}</dt>
91   <dd>An object that describes input from joystick and shoulder trigger
92   movements. Motion events are accompanied by an action code and a set of
93<em>axis values</em>. The action code specifies the state change that occurred
94such as a joystick being moved. The axis values describe the position and other
95movement properties for a specific physical control, such as
96{@link android.view.MotionEvent#AXIS_X} or
97{@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code
98by calling {@link android.view.MotionEvent#getAction()} and the axis value by
99calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}.
100<dd>
101</dl>
102<p>This lesson focuses on how you can handle input from the most common types of
103physical controls (gamepad buttons, directional pads, and
104joysticks) in a game screen by implementing the above-mentioned
105{@link android.view.View} callback methods and processing
106{@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p>
107
108<h2 id="input">Verify a Game Controller is Connected</h2>
109<p>When reporting input events, Android does not distinguish
110between events that came from a non-game controller device and events that came
111from a game controller. For example, a touch screen action generates an
112{@link android.view.MotionEvent#AXIS_X} event that represents the X
113coordinate of the touch surface, but a joystick generates an {@link android.view.MotionEvent#AXIS_X} event that represents the X position of the joystick. If
114your game cares about handling game-controller input, you should first check
115that the input event comes from a relevant source type.</p>
116<p>To verify that a connected input device is a game controller, call
117{@link android.view.InputDevice#getSources()} to obtain a combined bit field of
118input source types supported on that device. You can then test to see if
119the following fields are set:</p>
120<ul>
121<li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates
122that the input device has gamepad buttons (for example,
123{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source
124type does not strictly indicate if the game controller has D-pad buttons,
125although most gamepads typically have directional controls.</li>
126<li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that
127the input device has D-pad buttons (for example,
128{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li>
129<li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK}
130indicates that the input device has analog control sticks (for example, a
131joystick that records movements along {@link android.view.MotionEvent#AXIS_X}
132and {@link android.view.MotionEvent#AXIS_Y}).</li>
133</ul>
134<p>The following code snippet shows a helper method that lets you check whether
135  the connected input devices are game controllers. If so, the method retrieves
136  the device IDs for the game controllers. You can then associate each device
137  ID with a player in your game, and process game actions for each connected
138  player separately. To learn more about supporting multiple game controllers
139  that are simultaneously connected on the same Android device, see
140  <a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p>
141<pre>
142public ArrayList<Integer> getGameControllerIds() {
143    ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
144    int[] deviceIds = InputDevice.getDeviceIds();
145    for (int deviceId : deviceIds) {
146        InputDevice dev = InputDevice.getDevice(deviceId);
147        int sources = dev.getSources();
148
149        // Verify that the device has gamepad buttons, control sticks, or both.
150        if (((sources &amp; InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
151                || ((sources &amp; InputDevice.SOURCE_JOYSTICK)
152                == InputDevice.SOURCE_JOYSTICK)) {
153            // This device is a game controller. Store its device ID.
154            if (!gameControllerDeviceIds.contains(deviceId)) {
155                gameControllerDeviceIds.add(deviceId);
156            }
157        }
158    }
159    return gameControllerDeviceIds;
160}
161</pre>
162<p>Additionally, you might want to check for individual input capabilities
163supported by a connected game controller. This might be useful, for example, if
164you want your game to use only input from the set of physical controls it
165understands.</p>
166<p>To detect if a specific key code or axis code is supported by a connected
167game controller, use these techniques:</p>
168<ul>
169<li>In Android 4.4 (API level 19) or higher, you can determine if a key code is
170supported on a connected game controller by calling
171{@link android.view.InputDevice#hasKeys(int...)}.</li>
172<li>In Android 3.1 (API level 12) or higher, you can find all available axes
173supported on a connected game controller by first calling
174{@link android.view.InputDevice#getMotionRanges()}. Then, on each
175{@link android.view.InputDevice.MotionRange} object returned, call
176{@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li>
177</ul>
178
179<h2 id="button">Process Gamepad Button Presses</h2>
180<p>Figure 1 shows how Android maps key codes and axis values to the physical
181controls on most game controllers.</p>
182<img src="{@docRoot}images/training/game-controller-profiles.png" alt=""
183id="figure1" />
184<p class="img-caption">
185  <strong>Figure 1.</strong> Profile for a generic game controller.
186</p>
187<p>The callouts in the figure refer to the following:</p>
188<div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;">
189<ol style="margin-left:30px;list-style:decimal;">
190<li>{@link android.view.MotionEvent#AXIS_HAT_X},
191{@link android.view.MotionEvent#AXIS_HAT_Y},
192{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP},
193{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN},
194{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT},
195{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT}
196</li>
197<li>{@link android.view.MotionEvent#AXIS_X},
198{@link android.view.MotionEvent#AXIS_Y},
199{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li>
200<li>{@link android.view.MotionEvent#AXIS_Z},
201{@link android.view.MotionEvent#AXIS_RZ},
202{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li>
203<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li>
204<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li>
205<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li>
206<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li>
207<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li>
208<li>{@link android.view.MotionEvent#AXIS_RTRIGGER},
209{@link android.view.MotionEvent#AXIS_THROTTLE}</li>
210<li>{@link android.view.MotionEvent#AXIS_LTRIGGER},
211{@link android.view.MotionEvent#AXIS_BRAKE}</li>
212<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li>
213</ol>
214</div>
215<p>Common key codes generated by gamepad button presses include
216 {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
217{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
218{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
219and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game
220controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER
221DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your
222game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()}
223or from key event callbacks such as
224{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()},
225and if it represents an event that is relevant to your game, process it as a
226game action. Table 1 lists the recommended game actions for the most common
227gamepad buttons.
228</p>
229
230<p class="table-caption" id="table1">
231  <strong>Table 1.</strong> Recommended game actions for gamepad
232buttons.</p>
233<table>
234  <tr>
235    <th scope="col">Game Action</th>
236    <th scope="col">Button Key Code</th>
237  </tr>
238  <tr>
239    <td>Start game in main menu, or pause/unpause during game</td>
240    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}<sup>*</sup></td>
241  </tr>
242  <tr>
243    <td>Display menu</td>
244    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}<sup>*</sup>
245      and {@link android.view.KeyEvent#KEYCODE_MENU}<sup>*</sup></td>
246  </tr>
247  <tr>
248    <td>Same as Android <em>Back</em> navigation behavior described in the
249      <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design
250      guide.</td>
251    <td>{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td>
252  </tr>
253  <tr>
254    <td>Navigate back to a previous item in a menu</td>
255    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</td>
256  </tr>
257  <tr>
258    <td>Confirm selection, or perform primary game action</td>
259    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and
260{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td>
261  </tr>
262</table>
263<p>
264<em>* Your game should not rely on the presence of the Start, Select, or Menu
265  buttons.</em>
266</p>
267
268<p class="note"><strong>Tip: </strong>Consider providing a configuration screen
269in your game to allow users to personalize their own game controller mappings for
270game actions.</p>
271
272<p>The following snippet shows how you might override
273{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to
274associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and
275{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses
276with a game action.
277</p>
278<pre>
279public class GameView extends View {
280    ...
281
282    &#64;Override
283    public boolean onKeyDown(int keyCode, KeyEvent event) {
284        boolean handled = false;
285        if ((event.getSource() &amp; InputDevice.SOURCE_GAMEPAD)
286                == InputDevice.SOURCE_GAMEPAD) {
287            if (event.getRepeatCount() == 0) {
288                switch (keyCode) {
289                    // Handle gamepad and D-pad button presses to
290                    // navigate the ship
291                    ...
292
293                    default:
294                         if (isFireKey(keyCode)) {
295                             // Update the ship object to fire lasers
296                             ...
297                             handled = true;
298                         }
299                     break;
300                }
301            }
302            if (handled) {
303                return true;
304            }
305        }
306        return super.onKeyDown(keyCode, event);
307    }
308
309    private static boolean isFireKey(int keyCode) {
310        // Here we treat Button_A and DPAD_CENTER as the primary action
311        // keys for the game.
312        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
313                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
314    }
315}
316</pre>
317
318<p class="note"><strong>Note: </strong>On Android 4.2 (API
319level 17) and lower, the system treats
320{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android
321<em>Back</em> key by default. If your app supports these Android
322versions, make sure to treat
323{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game
324action. To determine the current Android SDK
325version on the device, refer to the
326{@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value.</p>
327
328<h2 id="dpad">Process Directional Pad Input</h2>
329<p>The 4-way directional pad (D-pad) is a common physical control in many game
330controllers. Android reports D-pad UP and DOWN presses as
331{@link android.view.MotionEvent#AXIS_HAT_Y} events with a range
332from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as
333{@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0
334(left) to 1.0 (right).</p>
335<p>Some controllers instead report D-pad presses with a key code. If your game
336cares about D-pad presses, you should treat the hat axis events and the D-pad
337key codes as the same input events, as recommended in table 2.</p>
338<p class="table-caption" id="table2">
339  <strong>Table 2.</strong> Recommended default game actions for D-pad key
340  codes and hat axis values.</p>
341<table>
342  <tr>
343    <th scope="col">Game Action</th>
344    <th scope="col">D-pad Key Code</th>
345    <th scope="col">Hat Axis Code</th>
346  </tr>
347  <tr>
348    <td>Move Up</td>
349    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td>
350    <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td>
351  </tr>
352  <tr>
353    <td>Move Down</td>
354    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td>
355    <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td>
356  </tr>
357  <tr>
358    <td>Move Left</td>
359    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td>
360    <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td>
361  </tr>
362  <tr>
363    <td>Move Right</td>
364    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td>
365    <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td>
366  </tr>
367</table>
368
369
370<p>The following code snippet shows a helper class that lets you check the hat
371axis and key code values from an input event to determine the D-pad direction.
372</p>
373<pre>
374public class Dpad {
375    final static int UP       = 0;
376    final static int LEFT     = 1;
377    final static int RIGHT    = 2;
378    final static int DOWN     = 3;
379    final static int CENTER   = 4;
380
381    int directionPressed = -1; // initialized to -1
382
383    public int getDirectionPressed(InputEvent event) {
384        if (!isDpadDevice(event)) {
385           return -1;
386        }
387
388        // If the input event is a MotionEvent, check its hat axis values.
389        if (event instanceof MotionEvent) {
390
391            // Use the hat axis value to find the D-pad direction
392            MotionEvent motionEvent = (MotionEvent) event;
393            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
394            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);
395
396            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
397            // LEFT and RIGHT direction accordingly.
398            if (Float.compare(xaxis, -1.0f) == 0) {
399                directionPressed =  Dpad.LEFT;
400            } else if (Float.compare(xaxis, 1.0f) == 0) {
401                directionPressed =  Dpad.RIGHT;
402            }
403            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
404            // UP and DOWN direction accordingly.
405            else if (Float.compare(yaxis, -1.0f) == 0) {
406                directionPressed =  Dpad.UP;
407            } else if (Float.compare(yaxis, 1.0f) == 0) {
408                directionPressed =  Dpad.DOWN;
409            }
410        }
411
412        // If the input event is a KeyEvent, check its key code.
413        else if (event instanceof KeyEvent) {
414
415           // Use the key code to find the D-pad direction.
416            KeyEvent keyEvent = (KeyEvent) event;
417            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
418                directionPressed = Dpad.LEFT;
419            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
420                directionPressed = Dpad.RIGHT;
421            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
422                directionPressed = Dpad.UP;
423            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
424                directionPressed = Dpad.DOWN;
425            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
426                directionPressed = Dpad.CENTER;
427            }
428        }
429        return directionPressed;
430    }
431
432    public static boolean isDpadDevice(InputEvent event) {
433        // Check that input comes from a device with directional pads.
434        if ((event.getSource() &amp; InputDevice.SOURCE_DPAD)
435             != InputDevice.SOURCE_DPAD) {
436             return true;
437         } else {
438             return false;
439         }
440     }
441}
442</pre>
443
444<p>You can use this helper class in your game wherever you want to process
445  D-pad input (for example, in the
446{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
447onGenericMotionEvent()} or
448{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}
449callbacks).</p>
450<p>For example:</p>
451<pre>
452Dpad mDpad = new Dpad();
453...
454&#64;Override
455public boolean onGenericMotionEvent(MotionEvent event) {
456
457    // Check if this event if from a D-pad and process accordingly.
458    if (Dpad.isDpadDevice(event)) {
459
460       int press = mDpad.getDirectionPressed(event);
461       switch (press) {
462            case LEFT:
463                // Do something for LEFT direction press
464                ...
465                return true;
466            case RIGHT:
467                // Do something for RIGHT direction press
468                ...
469                return true;
470            case UP:
471                // Do something for UP direction press
472                ...
473                return true;
474            ...
475        }
476    }
477
478    // Check if this event is from a joystick movement and process accordingly.
479    ...
480}
481</pre>
482
483<h2 id="joystick">Process Joystick Movements</h2>
484<p>When players move a joystick on their game controllers, Android reports a
485{@link android.view.MotionEvent} that contains the
486{@link android.view.MotionEvent#ACTION_MOVE} action code and the updated
487positions of the joystick's axes. Your game can use the data provided by
488the {@link android.view.MotionEvent} to determine if a joystick movement it
489cares about happened.
490</p>
491<p>Note that joystick motion events may batch multiple movement samples together
492within a single object. The {@link android.view.MotionEvent} object contains
493the current position for each joystick axis as well as multiple historical
494positions for each axis.  When reporting motion events with action code {@link android.view.MotionEvent#ACTION_MOVE} (such as joystick movements), Android batches up the
495axis values for efficiency. The historical values for an axis consists of the
496set of distinct values older than the current axis value, and more recent than
497values reported in any previous motion events. See the
498{@link android.view.MotionEvent} reference for details.</p>
499<p>You can use the historical information to more accurately render a game
500object's movement based on the joystick input. To
501retrieve the current and historical values, call
502{@link android.view.MotionEvent#getAxisValue(int)
503getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int,
504int) getHistoricalAxisValue()}. You can also find the number of historical
505points in the joystick event by calling
506{@link android.view.MotionEvent#getHistorySize()}.</p>
507<p>The following snippet shows how you might override the
508{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
509onGenericMotionEvent()} callback to process joystick input. You should first
510process the historical values for an axis, then process its current position.
511</p>
512<pre>
513public class GameView extends View {
514
515    &#64;Override
516    public boolean onGenericMotionEvent(MotionEvent event) {
517
518        // Check that the event came from a game controller
519        if ((event.getSource() &amp; InputDevice.SOURCE_JOYSTICK) ==
520                InputDevice.SOURCE_JOYSTICK &amp;&amp;
521                event.getAction() == MotionEvent.ACTION_MOVE) {
522
523            // Process all historical movement samples in the batch
524            final int historySize = event.getHistorySize();
525
526            // Process the movements starting from the
527            // earliest historical position in the batch
528            for (int i = 0; i &lt; historySize; i++) {
529                // Process the event at historical position i
530                processJoystickInput(event, i);
531            }
532
533            // Process the current movement sample in the batch (position -1)
534            processJoystickInput(event, -1);
535            return true;
536        }
537        return super.onGenericMotionEvent(event);
538    }
539}
540</pre>
541<p>Before using joystick input, you need to determine if the joystick is
542centered, then calculate its axis movements accordingly. Joysticks typically
543have a <em>flat</em> area, that is, a range of values near the (0,0) coordinate
544at which the axis is considered to be centered. If the axis value reported by
545Android falls within the flat area, you should treat the controller to be at
546rest (that is, motionless along both axes).</p>
547<p>The snippet below shows a helper method that calculates the movement along
548each axis. You invoke this helper in the {@code processJoystickInput()} method
549described further below.
550</p>
551<pre>
552private static float getCenteredAxis(MotionEvent event,
553        InputDevice device, int axis, int historyPos) {
554    final InputDevice.MotionRange range =
555            device.getMotionRange(axis, event.getSource());
556
557    // A joystick at rest does not always report an absolute position of
558    // (0,0). Use the getFlat() method to determine the range of values
559    // bounding the joystick axis center.
560    if (range != null) {
561        final float flat = range.getFlat();
562        final float value =
563                historyPos &lt; 0 ? event.getAxisValue(axis):
564                event.getHistoricalAxisValue(axis, historyPos);
565
566        // Ignore axis values that are within the 'flat' region of the
567        // joystick axis center.
568        if (Math.abs(value) > flat) {
569            return value;
570        }
571    }
572    return 0;
573}
574</pre>
575<p>Putting it all together, here is how you might process joystick movements in
576your game:</p>
577<pre>
578private void processJoystickInput(MotionEvent event,
579        int historyPos) {
580
581    InputDevice mInputDevice = event.getDevice();
582
583    // Calculate the horizontal distance to move by
584    // using the input value from one of these physical controls:
585    // the left control stick, hat axis, or the right control stick.
586    float x = getCenteredAxis(event, mInputDevice,
587            MotionEvent.AXIS_X, historyPos);
588    if (x == 0) {
589        x = getCenteredAxis(event, mInputDevice,
590                MotionEvent.AXIS_HAT_X, historyPos);
591    }
592    if (x == 0) {
593        x = getCenteredAxis(event, mInputDevice,
594                MotionEvent.AXIS_Z, historyPos);
595    }
596
597    // Calculate the vertical distance to move by
598    // using the input value from one of these physical controls:
599    // the left control stick, hat switch, or the right control stick.
600    float y = getCenteredAxis(event, mInputDevice,
601            MotionEvent.AXIS_Y, historyPos);
602    if (y == 0) {
603        y = getCenteredAxis(event, mInputDevice,
604                MotionEvent.AXIS_HAT_Y, historyPos);
605    }
606    if (y == 0) {
607        y = getCenteredAxis(event, mInputDevice,
608                MotionEvent.AXIS_RZ, historyPos);
609    }
610
611    // Update the ship object based on the new x and y values
612}
613</pre>
614<p>To support game controllers that have more sophisticated
615features beyond a single joystick, follow these best practices: </p>
616<ul>
617<li><strong>Handle dual controller sticks.</strong> Many game controllers have
618both a left and right joystick. For the left stick, Android
619reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events
620and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events.
621For the right stick, Android reports horizontal movements as
622{@link android.view.MotionEvent#AXIS_Z} events and vertical movements as
623{@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle
624both controller sticks in your code.</li>
625<li><strong>Handle shoulder trigger presses (but provide alternative input
626methods).</strong> Some controllers have left and right shoulder
627triggers. If these triggers are present, Android reports a left trigger press
628as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a
629right trigger press as an
630{@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android
6314.3 (API level 18), a controller that produces a
632{@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an
633identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The
634same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and
635{@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger
636presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not
637all controllers have triggers, so consider allowing players to perform those
638game actions with other buttons.
639</li>
640</ul>