summaryrefslogtreecommitdiffstats
path: root/docs/html/training/game-controllers/controller-input.jd
blob: c9517ba1dbd78513f1b2eadea99d2ea819d09dc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
page.title=Handling Controller Actions
trainingnavtop=true

@jd:body

<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">

<h2>This lesson teaches you to</h2>
<ol>
  <li><a href="#input">Verify a Game Controller is Connected</a></li>
  <li><a href="#button">Process Gamepad Button Presses</a>
  </li>
  <li><a href="#dpad">Process Directional Pad Input</a>
  </li>
  <li><a href="#joystick">Process Joystick Movements</a>
  </li>
</ol>

<h2>Try it out</h2>
<div class="download-box">
  <a href="http://developer.android.com/shareables/training/ControllerSample.zip"
class="button">Download the sample</a>
  <p class="filename">ControllerSample.zip</p>
</div>

</div>
</div>

<p>At the system level, Android reports input event codes from game controllers
as Android key codes and axis values. In your game, you can receive these codes
and values and convert them to specific in-game actions.</p>

<p>When players physically connect or wirelessly pair a game controller to
their Android-powered devices, the system auto-detects the controller
as an input device and starts reporting its input events. Your game can receive
these input events by implementing the following callback methods in your active
{@link android.app.Activity} or focused {@link android.view.View} (you should
implement the callbacks for either the {@link android.app.Activity} or
{@link android.view.View}, but not both): </p>

<ul>
<li>From {@link android.app.Activity}:
    <ul>
    <li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent) dispatchGenericMotionEvent(android.view.MotionEvent)}
        <p>Called to process generic motion events such as joystick movements.</p>
    </li>
    <li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) dispatchKeyEvent(android.view.KeyEvent)}
        <p>Called to process key events such as a press or release of a
          gamepad or D-pad button.</p>
    </li>
    </ul>
</li>
<li>From {@link android.view.View}:
    <ul>
    <li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
onGenericMotionEvent(android.view.MotionEvent)}
        <p>Called to process generic motion events such as joystick movements.</p>
    </li>
    <li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)}
        <p>Called to process a press of a physical key such as a gamepad or
          D-pad button.</p>
    </li>
    <li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)}
        <p>Called to process a release of a physical key such as a gamepad or
          D-pad button.</p>
    </li>
    </ul>
</li>
</ul>

<p>The recommended approach is to capture the events from the
  specific {@link android.view.View} object that the user interacts with.
  Inspect the following objects provided by the callbacks to get information
  about the type of input event received:</p>

<dl>
<dt>{@link android.view.KeyEvent}</dt>
   <dd>An object that describes directional
pad</a> (D-pad) and gamepad button events. Key events are accompanied by a
<em>key code</em> that indicates the specific button triggered, such as
{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}
or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the
key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key
event callbacks such as
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}.
<dd>
<dt>{@link android.view.MotionEvent}</dt>
   <dd>An object that describes input from joystick and shoulder trigger
   movements. Motion events are accompanied by an action code and a set of
<em>axis values</em>. The action code specifies the state change that occurred
such as a joystick being moved. The axis values describe the position and other
movement properties for a specific physical control, such as
{@link android.view.MotionEvent#AXIS_X} or
{@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code
by calling {@link android.view.MotionEvent#getAction()} and the axis value by
calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}.
<dd>
</dl>
<p>This lesson focuses on how you can handle input from the most common types of
physical controls (gamepad buttons, directional pads, and
joysticks) in a game screen by implementing the above-mentioned
{@link android.view.View} callback methods and processing
{@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p>

<h2 id="input">Verify a Game Controller is Connected</h2>
<p>When reporting input events, Android does not distinguish
between events that came from a non-game controller device and events that came
from a game controller. For example, a touch screen action generates an
{@link android.view.MotionEvent#AXIS_X} event that represents the X
coordinate 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
your game cares about handling game-controller input, you should first check
that the input event comes from a relevant source type.</p>
<p>To verify that a connected input device is a game controller, call
{@link android.view.InputDevice#getSources()} to obtain a combined bit field of
input source types supported on that device. You can then test to see if
the following fields are set:</p>
<ul>
<li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates
that the input device has gamepad buttons (for example,
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source
type does not strictly indicate if the game controller has D-pad buttons,
although most gamepads typically have directional controls.</li>
<li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that
the input device has D-pad buttons (for example,
{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li>
<li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK}
indicates that the input device has analog control sticks (for example, a
joystick that records movements along {@link android.view.MotionEvent#AXIS_X}
and {@link android.view.MotionEvent#AXIS_Y}).</li>
</ul>
<p>The following code snippet shows a helper method that lets you check whether
  the connected input devices are game controllers. If so, the method retrieves
  the device IDs for the game controllers. You can then associate each device
  ID with a player in your game, and process game actions for each connected
  player separately. To learn more about supporting multiple game controllers
  that are simultaneously connected on the same Android device, see
  <a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p>
<pre>
public ArrayList<Integer> getGameControllerIds() {
    ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
    int[] deviceIds = InputDevice.getDeviceIds();
    for (int deviceId : deviceIds) {
        InputDevice dev = InputDevice.getDevice(deviceId);
        int sources = dev.getSources();

        // Verify that the device has gamepad buttons, control sticks, or both.
        if (((sources &amp; InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                || ((sources &amp; InputDevice.SOURCE_JOYSTICK)
                == InputDevice.SOURCE_JOYSTICK)) {
            // This device is a game controller. Store its device ID.
            if (!gameControllerDeviceIds.contains(deviceId)) {
                gameControllerDeviceIds.add(deviceId);
            }
        }
    }
    return gameControllerDeviceIds;
}
</pre>
<p>Additionally, you might want to check for individual input capabilities
supported by a connected game controller. This might be useful, for example, if
you want your game to use only input from the set of physical controls it
understands.</p>
<p>To detect if a specific key code or axis code is supported by a connected
game controller, use these techniques:</p>
<ul>
<li>In Android 4.4 (API level 19) or higher, you can determine if a key code is
supported on a connected game controller by calling
{@link android.view.InputDevice#hasKeys(int...)}.</li>
<li>In Android 3.1 (API level 12) or higher, you can find all available axes
supported on a connected game controller by first calling
{@link android.view.InputDevice#getMotionRanges()}. Then, on each
{@link android.view.InputDevice.MotionRange} object returned, call
{@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li>
</ul>

<h2 id="button">Process Gamepad Button Presses</h2>
<p>Figure 1 shows how Android maps key codes and axis values to the physical
controls on most game controllers.</p>
<img src="{@docRoot}images/training/game-controller-profiles.png" alt=""
id="figure1" />
<p class="img-caption">
  <strong>Figure 1.</strong> Profile for a generic game controller.
</p>
<p>The callouts in the figure refer to the following:</p>
<div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;">
<ol style="margin-left:30px;list-style:decimal;">
<li>{@link android.view.MotionEvent#AXIS_HAT_X},
{@link android.view.MotionEvent#AXIS_HAT_Y},
{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP},
{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN},
{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT},
{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT}
</li>
<li>{@link android.view.MotionEvent#AXIS_X},
{@link android.view.MotionEvent#AXIS_Y},
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li>
<li>{@link android.view.MotionEvent#AXIS_Z},
{@link android.view.MotionEvent#AXIS_RZ},
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li>
<li>{@link android.view.MotionEvent#AXIS_RTRIGGER},
{@link android.view.MotionEvent#AXIS_THROTTLE}</li>
<li>{@link android.view.MotionEvent#AXIS_LTRIGGER},
{@link android.view.MotionEvent#AXIS_BRAKE}</li>
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li>
</ol>
</div>
<p>Common key codes generated by gamepad button presses include
 {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game
controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER
DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your
game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()}
or from key event callbacks such as
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()},
and if it represents an event that is relevant to your game, process it as a
game action. Table 1 lists the recommended game actions for the most common
gamepad buttons.
</p>

<p class="table-caption" id="table1">
  <strong>Table 1.</strong> Recommended game actions for gamepad
buttons.</p>
<table>
  <tr>
    <th scope="col">Game Action</th>
    <th scope="col">Button Key Code</th>
  </tr>
  <tr>
    <td>Start game in main menu, or pause/unpause during game</td>
    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}<sup>*</sup></td>
  </tr>
  <tr>
    <td>Display menu</td>
    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}<sup>*</sup>
      and {@link android.view.KeyEvent#KEYCODE_MENU}<sup>*</sup></td>
  </tr>
  <tr>
    <td>Same as Android <em>Back</em></td>
    <td>{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td>
  </tr>
  <tr>
    <td>Navigate back to a previous item in a menu</td>
    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>**</sup></td>
  </tr>
  <tr>
    <td>Confirm selection, or perform primary game action</td>
    <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>**</sup> and
{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td>
  </tr>
</table>
<p>
<em>* Your game should not rely on the presence of the Start, Select, or Menu
  buttons.</em>
</p>
<p>
<em>** This could be the opposite button (A/B), depending on the locale that
you are supporting.</em>
</p>

<p class="note"><strong>Tip: </strong>Consider providing a configuration screen
in your game to allow users to personalize their own game controller mappings for
game actions.</p>

<p>The following snippet shows how you might override
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to
associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and
{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses
with a game action.
</p>
<pre>
public class GameView extends View {
    ...

    &#64;Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() &amp; InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship
                    ...

                    default:
                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             ...
                             handled = true;
                         }
                     break;
                }
            }
            if (handled) {
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game. You may need to switch this to Button_B and
        // DPAD_CENTER depending on the user expectations for the locale
        // in which your game runs.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
    }
}
</pre>

<p>Follow these best practices when handling button presses:</p>
<ul>
<li><strong>Provide localized button mappings.</strong> Generally, if your game
has a primary gameplay action (for example, it fires lasers, lets your avatar
do a high jump, or confirms an item selection), you should map
both {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} and
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to this action. However,
in some locales, users may expect
{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B} to be the confirm
button and {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to be the
back button instead. If you are supporting these locales, make sure to treat
the A and B buttons accordingly in your game. To determine the user's locale,
call the {@link java.util.Locale#getDefault()} method.
<li><strong>Map {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}
consistently across different Android versions.</strong> On Android 4.2 (API
level 17) and lower, the system treats
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android
<em>Back</em> key by default. If your app supports these Android
versions, make sure to treat
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game
action (except in the localization case mentioned
above). To determine the current Android SDK
version on the device, refer to the
{@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value.
</li>
</ul>

<h2 id="dpad">Process Directional Pad Input</h2>
<p>The 4-way directional pad (D-pad) is a common physical control in many game
controllers. Android reports D-pad UP and DOWN presses as
{@link android.view.MotionEvent#AXIS_HAT_Y} events with a range
from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as
{@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0
(left) to 1.0 (right).</p>
<p>Some controllers instead report D-pad presses with a key code. If your game
cares about D-pad presses, you should treat the hat axis events and the D-pad
key codes as the same input events, as recommended in table 2.</p>
<p class="table-caption" id="table2">
  <strong>Table 2.</strong> Recommended default game actions for D-pad key
  codes and hat axis values.</p>
<table>
  <tr>
    <th scope="col">Game Action</th>
    <th scope="col">D-pad Key Code</th>
    <th scope="col">Hat Axis Code</th>
  </tr>
  <tr>
    <td>Move Up</td>
    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td>
    <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td>
  </tr>
  <tr>
    <td>Move Down</td>
    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td>
    <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td>
  </tr>
  <tr>
    <td>Move Left</td>
    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td>
    <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td>
  </tr>
  <tr>
    <td>Move Right</td>
    <td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td>
    <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td>
  </tr>
</table>


<p>The following code snippet shows a helper class that lets you check the hat
axis and key code values from an input event to determine the D-pad direction.
</p>
<pre>
public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;
        }

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (Float.compare(xaxis, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (Float.compare(xaxis, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            }
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.DOWN;
            }
        }

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
            }
        }
        return directionPressed;
    }

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        if ((event.getSource() &amp; InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;
         }
     }
}
</pre>

<p>You can use this helper class in your game wherever you want to process
  D-pad input (for example, in the
{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
onGenericMotionEvent()} or
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}
callbacks).</p>
<p>For example:</p>
<pre>
Dpad mDpad = new Dpad();
...
&#64;Override
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = mDpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                ...
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                ...
                return true;
            case UP:
                // Do something for UP direction press
                ...
                return true;
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}
</pre>

<h2 id="joystick">Process Joystick Movements</h2>
<p>When players move a joystick on their game controllers, Android reports a
{@link android.view.MotionEvent} that contains the
{@link android.view.MotionEvent#ACTION_MOVE} action code and the updated
positions of the joystick's axes. Your game can use the data provided by
the {@link android.view.MotionEvent} to determine if a joystick movement it
cares about happened.
</p>
<p>Note that joystick motion events may batch multiple movement samples together
within a single object. The {@link android.view.MotionEvent} object contains
the current position for each joystick axis as well as multiple historical
positions for each axis.  When reporting motion events with action code {@link android.view.MotionEvent#ACTION_MOVE} (such as joystick movements), Android batches up the
axis values for efficiency. The historical values for an axis consists of the
set of distinct values older than the current axis value, and more recent than
values reported in any previous motion events. See the
{@link android.view.MotionEvent} reference for details.</p>
<p>You can use the historical information to more accurately render a game
object's movement based on the joystick input. To
retrieve the current and historical values, call
{@link android.view.MotionEvent#getAxisValue(int)
getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int,
int) getHistoricalAxisValue()}. You can also find the number of historical
points in the joystick event by calling
{@link android.view.MotionEvent#getHistorySize()}.</p>
<p>The following snippet shows how you might override the
{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
onGenericMotionEvent()} callback to process joystick input. You should first
process the historical values for an axis, then process its current position.
</p>
<pre>
public class GameView extends View {

    &#64;Override
    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() &amp; InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &amp;&amp;
                event.getAction() == MotionEvent.ACTION_MOVE)

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i &lt; historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        }
        return super.onGenericMotionEvent(event);
    }
}
</pre>
<p>Before using joystick input, you need to determine if the joystick is
centered, then calculate its axis movements accordingly. Joysticks typically
have a <em>flat</em> area, that is, a range of values near the (0,0) coordinate
at which the axis is considered to be centered. If the axis value reported by
Android falls within the flat area, you should treat the controller to be at
rest (that is, motionless along both axes).</p>
<p>The snippet below shows a helper method that calculates the movement along
each axis. You invoke this helper in the {@code processJoystickInput()} method
described further below.
</p>
<pre>
private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos &lt; 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
        }
    }
    return 0;
}
</pre>
<p>Putting it all together, here is how you might process joystick movements in
your game:</p>
<pre>
private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice mInputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, mInputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    }
    if (x == 0) {
        x = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_Z, historyPos);
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, mInputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    }
    if (y == 0) {
        y = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_RZ, historyPos);
    }

    // Update the ship object based on the new x and y values
    ...

    return true;
}
</pre>
<p>To support game controllers that have more sophisticated
features beyond a single joystick, follow these best practices: </p>
<ul>
<li><strong>Handle dual controller sticks.</strong> Many game controllers have
both a left and right joystick. For the left stick, Android
reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events
and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events.
For the right stick, Android reports horizontal movements as
{@link android.view.MotionEvent#AXIS_Z} events and vertical movements as
{@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle
both controller sticks in your code.</li>
<li><strong>Handle shoulder trigger presses (but provide alternative input
methods).</strong> Some controllers have left and right shoulder
triggers. If these triggers are present, Android reports a left trigger press
as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a
right trigger press as an
{@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android
4.3 (API level 18), a controller that produces a
{@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an
identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The
same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and
{@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger
presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not
all controllers have triggers, so consider allowing players to perform those
game actions with other buttons.
</li>
</ul>