1page.title=Supporting Controllers Across Android Versions
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="#prepare">Prepare to Abstract APIs for Game Controller
13Suppport</a></li>
14  <li><a href="#abstraction">Add an Interface for Backward Compatibility</a></li>
15  <li><a href="#newer">Implement the Interface on Android 4.1 and Higher</a></li>
16  <li><a href="#older">Implement the Interface on Android 2.3 up to Android
174.0</a></li>
18  <li><a href="#using">Use the Version-Specific Implementations</a></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>If you are supporting game controllers in your game, it's your responsibility
32to make sure that your game responds to controllers consistently across devices
33running on different versions of Android. This lets your game reach a wider
34audience, and your players can enjoy a seamless gameplay experience with
35their controllers even when they switch or upgrade their Android devices.</p>
36
37<p>This lesson demonstrates how to use APIs available in Android 4.1 and higher
38in a backward compatible way, enabling your game to support the following
39features on devices running Android 2.3 and higher:</p>
40<ul>
41<li>The game can detect if a new game controller is added, changed, or removed.</li>
42<li>The game can query the capabilities of a game controller.</li>
43<li>The game can recognize incoming motion events from a game controller.</li>
44</ul>
45
46<p>The examples in this lesson are based on the reference implementation
47provided by the sample {@code ControllerSample.zip} available for download
48above. This sample shows how to implement the {@code InputManagerCompat}
49interface to support different versions of Android. To compile the sample, you
50must use Android 4.1 (API level 16) or higher. Once compiled, the sample app
51runs on any device running Android 2.3 (API level 9) or higher as the build
52target.
53</p>
54
55<h2 id="prepare">Prepare to Abstract APIs for Game Controller Support</h2>
56<p>Suppose you want to be able to determine if a game controller's connection
57status has changed on devices running on Android 2.3 (API level 9). However,
58the APIs are only available in Android 4.1 (API level 16) and higher, so you
59need to provide an implementation that supports Android 4.1 and higher while
60providing a fallback mechanism that supports Android 2.3 up to Android 4.0.</p>
61
62<p>To help you determine which features require such a fallback mechanism for
63    older versions, table 1 lists the differences in game controller support
64    between Android 2.3 (API level 9), 3.1 (API level 12), and 4.1 (API level
65    16).</p>
66
67<p class="table-caption" id="game-controller-support-table">
68<strong>Table 1.</strong> APIs for game controller support across
69different Android versions.
70</p>
71
72<table>
73<tbody>
74<tr>
75<th>Controller Information</th>
76<th>Controller API</th>
77<th>API level 9</th>
78<th>API level 12</th>
79<th>API level 16</th>
80</tr>
81
82<tr>
83<td rowspan="5">Device Identification</td>
84<td>{@link android.hardware.input.InputManager#getInputDeviceIds()}</td>
85<td style="text-align: center;"><big>&nbsp;</big></td>
86<td style="text-align: center;"><big>&nbsp;</big></td>
87<td style="text-align: center;"><big>&bull;</big></td>
88</tr>
89
90<tr>
91<td>{@link android.hardware.input.InputManager#getInputDevice(int)
92getInputDevice()}</td>
93<td style="text-align: center;">&nbsp;</td>
94<td style="text-align: center;"><big>&nbsp;</big></td>
95<td style="text-align: center;"><big>&bull;</big></td>
96</tr>
97
98<tr>
99<td>{@link android.view.InputDevice#getVibrator()}</td>
100<td style="text-align: center;">&nbsp;</td>
101<td style="text-align: center;"><big>&nbsp;</big></td>
102<td style="text-align: center;"><big>&bull;</big></td>
103</tr>
104
105<td>{@link android.view.InputDevice#SOURCE_JOYSTICK}</td>
106<td style="text-align: center;">&nbsp;</td>
107<td style="text-align: center;"><big>&bull;</big></td>
108<td style="text-align: center;"><big>&bull;</big></td>
109</tr>
110
111<tr>
112<td>{@link android.view.InputDevice#SOURCE_GAMEPAD}</td>
113<td style="text-align: center;">&nbsp;</td>
114<td style="text-align: center;"><big>&bull;</big></td>
115<td style="text-align: center;"><big>&bull;</big></td>
116</tr>
117
118<tr>
119<td rowspan="3">Connection Status</td>
120<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceAdded(int) onInputDeviceAdded()}</td>
121<td style="text-align: center;">&nbsp;</td>
122<td style="text-align: center;">&nbsp;</td>
123<td style="text-align: center;"><big>&bull;</big></td>
124</tr>
125
126<tr>
127<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceChanged(int) onInputDeviceChanged()}</td>
128<td style="text-align: center;">&nbsp;</td>
129<td style="text-align: center;">&nbsp;</td>
130<td style="text-align: center;"><big>&bull;</big></td>
131</tr>
132
133<tr>
134<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceRemoved(int) onInputDeviceRemoved()}</td>
135<td style="text-align: center;">&nbsp;</td>
136<td style="text-align: center;">&nbsp;</td>
137<td style="text-align: center;"><big>&bull;</big></td>
138</tr>
139
140<tr>
141<td rowspan="4">Input Event Identification</td>
142<td>D-pad press (
143{@link android.view.KeyEvent#KEYCODE_DPAD_UP},
144{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN},
145{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT},
146{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT},
147{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER})</td>
148<td style="text-align: center;"><big>&bull;</big></td>
149<td style="text-align: center;"><big>&bull;</big></td>
150<td style="text-align: center;"><big>&bull;</big></td>
151</tr>
152
153<tr>
154<td>Gamepad button press (
155{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
156{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
157{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL},
158{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR},
159{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
160{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START},
161{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1},
162{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1},
163{@link android.view.KeyEvent#KEYCODE_BUTTON_R2 BUTTON_R2},
164{@link android.view.KeyEvent#KEYCODE_BUTTON_L2 BUTTON_L2})</td>
165<td style="text-align: center;">&nbsp;</td>
166<td style="text-align: center;"><big>&bull;</big></td>
167<td style="text-align: center;"><big>&bull;</big></td>
168</tr>
169
170<tr>
171<td>Joystick and hat switch movement (
172{@link android.view.MotionEvent#AXIS_X},
173{@link android.view.MotionEvent#AXIS_Y},
174{@link android.view.MotionEvent#AXIS_Z},
175{@link android.view.MotionEvent#AXIS_RZ},
176{@link android.view.MotionEvent#AXIS_HAT_X},
177{@link android.view.MotionEvent#AXIS_HAT_Y})</td>
178<td style="text-align: center;">&nbsp;</td>
179<td style="text-align: center;"><big>&bull;</big></td>
180<td style="text-align: center;"><big>&bull;</big></td>
181</tr>
182
183<tr>
184<td>Analog trigger press (
185{@link android.view.MotionEvent#AXIS_LTRIGGER},
186{@link android.view.MotionEvent#AXIS_RTRIGGER})</td>
187<td style="text-align: center;">&nbsp;</td>
188<td style="text-align: center;"><big>&bull;</big></td>
189<td style="text-align: center;"><big>&bull;</big></td>
190</tr>
191
192</tbody>
193</table>
194
195<p>You can use abstraction to build version-aware game controller support that
196works across platforms. This approach involves the following steps:</p>
197<ol>
198<li>Define an intermediary Java interface that abstracts the implementation of
199the game controller features required by your game.</li>
200<li>Create a proxy implementation of your interface that uses APIs in Android
2014.1 and higher.</li>
202<li>Create a custom implementation of your interface that uses APIs available
203between Android 2.3 up to Android 4.0.</li>
204<li>Create the logic for switching between these implementations at runtime,
205and begin using the interface in your game.</li>
206</ol>
207
208<p>For an overview of how abstraction can be used to ensure that applications
209can work in a backward compatible way across different versions of Android, see
210<a href="{@docRoot}training/backward-compatible-ui/index.html">Creating
211Backward-Compatible UIs</a>.
212</p>
213
214<h2 id="abstraction">Add an Interface for Backward Compatibility</h2>
215
216<p>To provide backward compatibility, you can create a custom interface then
217add version-specific implementations. One advantage of this approach is that it
218lets you mirror the public interfaces on Android 4.1 (API level 16) that
219support game controllers.</p>
220<pre>
221// The InputManagerCompat interface is a reference example.
222// The full code is provided in the ControllerSample.zip sample.
223public interface InputManagerCompat {
224    ...
225    public InputDevice getInputDevice(int id);
226    public int[] getInputDeviceIds();
227
228    public void registerInputDeviceListener(
229            InputManagerCompat.InputDeviceListener listener,
230            Handler handler);
231    public void unregisterInputDeviceListener(
232            InputManagerCompat.InputDeviceListener listener);
233
234    public void onGenericMotionEvent(MotionEvent event);
235
236    public void onPause();
237    public void onResume();
238
239    public interface InputDeviceListener {
240        void onInputDeviceAdded(int deviceId);
241        void onInputDeviceChanged(int deviceId);
242        void onInputDeviceRemoved(int deviceId);
243    }
244    ...
245}
246</pre>
247<p>The {@code InputManagerCompat} interface provides the following methods:</p>
248<dl>
249<dt>{@code getInputDevice()}</dt>
250<dd>Mirrors {@link android.hardware.input.InputManager#getInputDevice(int)
251getInputDevice()}. Obtains the {@link android.view.InputDevice}
252object that represents the capabilities of a game controller.</dd>
253<dt>{@code getInputDeviceIds()}</dt>
254<dd>Mirrors {@link android.hardware.input.InputManager#getInputDeviceIds()
255getInputDeviceIds()}. Returns an array of integers, each of
256which is an ID for a different input device. This is useful if you're building
257a game that supports multiple players and you want to detect how many
258controllers are connected.</dd>
259<dt>{@code registerInputDeviceListener()}</dt>
260<dd>Mirrors {@link android.hardware.input.InputManager#registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler)
261registerInputDeviceListener()}. Lets you register to be informed when a new
262device is added, changed, or removed.</dd>
263<dt>{@code unregisterInputDeviceListener()}</dt>
264<dd>Mirrors {@link android.hardware.input.InputManager#unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener) unregisterInputDeviceListener()}.
265Unregisters an input device listener.</dd>
266<dt>{@code onGenericMotionEvent()}</dt>
267<dd>Mirrors {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
268onGenericMotionEvent()}. Lets your game intercept and handle
269{@link android.view.MotionEvent} objects and axis values that represent events
270such as joystick movements and analog trigger presses.</dd>
271<dt>{@code onPause()}</dt>
272<dd>Stops polling for game controller events when the
273main activity is paused, or when the game no longer has focus.</dd>
274<dt>{@code onResume()}</dt>
275<dd>Starts polling for game controller events when the
276main activity is resumed, or when the game is started and runs in the
277foreground.</dd>
278<dt>{@code InputDeviceListener}</dt>
279<dd>Mirrors the {@link android.hardware.input.InputManager.InputDeviceListener}
280interface. Lets your game know when a game controller has been added, changed, or
281removed.</dd>
282</dl>
283<p>Next, create implementations for {@code InputManagerCompat} that work
284across different platform versions. If your game is running on Android 4.1 or
285higher and calls an {@code InputManagerCompat} method, the proxy implementation
286calls the equivalent method in {@link android.hardware.input.InputManager}.
287However, if your game is running on Android 2.3 up to Android 4.0, the custom implementation processes calls to {@code InputManagerCompat} methods by using
288only APIs introduced no later than Android 2.3. Regardless of which
289version-specific implementation is used at runtime, the implementation passes
290the call results back transparently to the game.</p>
291
292<img src="{@docRoot}images/training/backward-compatible-inputmanager.png" alt=""
293id="figure1" />
294<p class="img-caption">
295  <strong>Figure 1.</strong> Class diagram of interface and version-specific
296implementations.
297</p>
298
299<h2 id="newer">Implement the Interface on Android 4.1 and Higher</h2>
300<p>{@code InputManagerCompatV16} is an implementation of the
301{@code InputManagerCompat} interface that proxies method calls to an
302actual {@link android.hardware.input.InputManager} and {@link
303android.hardware.input.InputManager.InputDeviceListener}. The
304{@link android.hardware.input.InputManager} is obtained from the system
305{@link android.content.Context}.</p>
306
307<pre>
308// The InputManagerCompatV16 class is a reference implementation.
309// The full code is provided in the ControllerSample.zip sample.
310public class InputManagerV16 implements InputManagerCompat {
311
312    private final InputManager mInputManager;
313    private final Map<InputManagerCompat.InputDeviceListener,
314            V16InputDeviceListener> mListeners;
315
316    public InputManagerV16(Context context) {
317        mInputManager = (InputManager)
318                context.getSystemService(Context.INPUT_SERVICE);
319        mListeners = new HashMap<InputManagerCompat.InputDeviceListener,
320                V16InputDeviceListener>();
321    }
322
323    &#64;Override
324    public InputDevice getInputDevice(int id) {
325        return mInputManager.getInputDevice(id);
326    }
327
328    &#64;Override
329    public int[] getInputDeviceIds() {
330        return mInputManager.getInputDeviceIds();
331    }
332
333    static class V16InputDeviceListener implements
334            InputManager.InputDeviceListener {
335        final InputManagerCompat.InputDeviceListener mIDL;
336
337        public V16InputDeviceListener(InputDeviceListener idl) {
338            mIDL = idl;
339        }
340
341        &#64;Override
342        public void onInputDeviceAdded(int deviceId) {
343            mIDL.onInputDeviceAdded(deviceId);
344        }
345
346        // Do the same for device change and removal
347        ...
348    }
349
350    &#64;Override
351    public void registerInputDeviceListener(InputDeviceListener listener,
352            Handler handler) {
353        V16InputDeviceListener v16Listener = new
354                V16InputDeviceListener(listener);
355        mInputManager.registerInputDeviceListener(v16Listener, handler);
356        mListeners.put(listener, v16Listener);
357    }
358
359    // Do the same for unregistering an input device listener
360    ...
361
362    &#64;Override
363    public void onGenericMotionEvent(MotionEvent event) {
364        // unused in V16
365    }
366
367    &#64;Override
368    public void onPause() {
369        // unused in V16
370    }
371
372    &#64;Override
373    public void onResume() {
374        // unused in V16
375    }
376
377}
378</pre>
379
380<h2 id="older">Implementing the Interface on Android 2.3 up to Android 4.0</h2>
381
382<p>The {@code InputManagerV9} implementation uses APIs introduced no later
383than Android 2.3. To create an implementation of {@code
384InputManagerCompat} that supports Android 2.3 up to Android 4.0, you can use
385the following objects:
386<ul>
387<li>A {@link android.util.SparseArray} of device IDs to track the
388game controllers that are connected to the device.</li>
389<li>A {@link android.os.Handler} to process device events. When an app is started
390or resumed, the {@link android.os.Handler} receives a message to start polling
391for game controller disconnection. The {@link android.os.Handler} will start a
392loop to check each known connected game controller and see if a device ID is
393returned. A {@code null} return value indicates that the game controller is
394disconnected. The {@link android.os.Handler} stops polling when the app is
395paused.</li>
396<li>A {@link java.util.Map} of {@code InputManagerCompat.InputDeviceListener}
397objects. You will use the listeners to update the connection status of tracked
398game controllers.</li>
399</ul>
400
401<pre>
402// The InputManagerCompatV9 class is a reference implementation.
403// The full code is provided in the ControllerSample.zip sample.
404public class InputManagerV9 implements InputManagerCompat {
405    private final SparseArray<long[]> mDevices;
406    private final Map<InputDeviceListener, Handler> mListeners;
407    private final Handler mDefaultHandler;
408409
410    public InputManagerV9() {
411        mDevices = new SparseArray<long[]>();
412        mListeners = new HashMap<InputDeviceListener, Handler>();
413        mDefaultHandler = new PollingMessageHandler(this);
414    }
415}
416</pre>
417
418<p>Implement a {@code PollingMessageHandler} object that extends
419{@link android.os.Handler}, and override the
420{@link android.os.Handler#handleMessage(android.os.Message) handleMessage()}
421method. This method checks if an attached game controller has been
422disconnected and notifies registered listeners.</p>
423
424<pre>
425private static class PollingMessageHandler extends Handler {
426    private final WeakReference<InputManagerV9> mInputManager;
427
428    PollingMessageHandler(InputManagerV9 im) {
429        mInputManager = new WeakReference<InputManagerV9>(im);
430    }
431
432    &#64;Override
433    public void handleMessage(Message msg) {
434        super.handleMessage(msg);
435        switch (msg.what) {
436            case MESSAGE_TEST_FOR_DISCONNECT:
437                InputManagerV9 imv = mInputManager.get();
438                if (null != imv) {
439                    long time = SystemClock.elapsedRealtime();
440                    int size = imv.mDevices.size();
441                    for (int i = 0; i &lt; size; i++) {
442                        long[] lastContact = imv.mDevices.valueAt(i);
443                        if (null != lastContact) {
444                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
445                                // check to see if the device has been
446                                // disconnected
447                                int id = imv.mDevices.keyAt(i);
448                                if (null == InputDevice.getDevice(id)) {
449                                    // Notify the registered listeners
450                                    // that the game controller is disconnected
451                                    ...
452                                    imv.mDevices.remove(id);
453                                } else {
454                                    lastContact[0] = time;
455                                }
456                            }
457                        }
458                    }
459                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
460                            CHECK_ELAPSED_TIME);
461                }
462                break;
463        }
464    }
465}
466</pre>
467
468<p>To start and stop polling for game controller disconnection, override
469these methods:</p>
470<pre>
471private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
472private static final long CHECK_ELAPSED_TIME = 3000L;
473
474&#64;Override
475public void onPause() {
476    mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
477}
478
479&#64;Override
480public void onResume() {
481    mDefaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
482            CHECK_ELAPSED_TIME);
483}
484</pre>
485
486<p>To detect that an input device has been added, override the
487{@code onGenericMotionEvent()} method. When the system reports a motion event,
488check if this event came from a device ID that is already tracked, or from a
489new device ID. If the device ID is new, notify registered listeners.</p>
490
491<pre>
492&#64;Override
493public void onGenericMotionEvent(MotionEvent event) {
494    // detect new devices
495    int id = event.getDeviceId();
496    long[] timeArray = mDevices.get(id);
497    if (null == timeArray) {
498        // Notify the registered listeners that a game controller is added
499        ...
500        timeArray = new long[1];
501        mDevices.put(id, timeArray);
502    }
503    long time = SystemClock.elapsedRealtime();
504    timeArray[0] = time;
505}
506</pre>
507
508<p>Notification of listeners is implemented by using the
509{@link android.os.Handler} object to send a {@code DeviceEvent}
510{@link java.lang.Runnable} object to the message queue. The {@code DeviceEvent}
511contains a reference to an {@code InputManagerCompat.InputDeviceListener}. When
512the {@code DeviceEvent} runs, the appropriate callback method of the listener
513is called to signal if the game controller was added, changed, or removed.
514</p>
515
516<pre>
517&#64;Override
518public void registerInputDeviceListener(InputDeviceListener listener,
519        Handler handler) {
520    mListeners.remove(listener);
521    if (handler == null) {
522        handler = mDefaultHandler;
523    }
524    mListeners.put(listener, handler);
525}
526
527&#64;Override
528public void unregisterInputDeviceListener(InputDeviceListener listener) {
529    mListeners.remove(listener);
530}
531
532private void notifyListeners(int why, int deviceId) {
533    // the state of some device has changed
534    if (!mListeners.isEmpty()) {
535        for (InputDeviceListener listener : mListeners.keySet()) {
536            Handler handler = mListeners.get(listener);
537            DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
538                    listener);
539            handler.post(odc);
540        }
541    }
542}
543
544private static class DeviceEvent implements Runnable {
545    private int mMessageType;
546    private int mId;
547    private InputDeviceListener mListener;
548    private static Queue<DeviceEvent> sObjectQueue =
549            new ArrayDeque<DeviceEvent>();
550    ...
551
552    static DeviceEvent getDeviceEvent(int messageType, int id,
553            InputDeviceListener listener) {
554        DeviceEvent curChanged = sObjectQueue.poll();
555        if (null == curChanged) {
556            curChanged = new DeviceEvent();
557        }
558        curChanged.mMessageType = messageType;
559        curChanged.mId = id;
560        curChanged.mListener = listener;
561        return curChanged;
562    }
563
564    &#64;Override
565    public void run() {
566        switch (mMessageType) {
567            case ON_DEVICE_ADDED:
568                mListener.onInputDeviceAdded(mId);
569                break;
570            case ON_DEVICE_CHANGED:
571                mListener.onInputDeviceChanged(mId);
572                break;
573            case ON_DEVICE_REMOVED:
574                mListener.onInputDeviceRemoved(mId);
575                break;
576            default:
577                // Handle unknown message type
578                ...
579                break;
580        }
581        // Put this runnable back in the queue
582        sObjectQueue.offer(this);
583    }
584}
585</pre>
586
587<p>You now have two implementations of {@code InputManagerCompat}: one that
588works on devices running Android 4.1 and higher, and another
589that works on devices running Android 2.3 up to Android 4.0.</p>
590
591<h2 id="using">Use the Version-Specific Implementation</h2>
592<p>The version-specific switching logic is implemented in a class that acts as
593a <a href="http://en.wikipedia.org/wiki/Factory_(software_concept)"
594class="external-link" target="_blank">factory</a>.</p>
595<pre>
596public static class Factory {
597    public static InputManagerCompat getInputManager(Context context) {
598        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
599            return new InputManagerV16(context);
600        } else {
601            return new InputManagerV9();
602        }
603    }
604}
605</pre>
606<p>Now you can simply instantiate an {@code InputManagerCompat} object and
607register an {@code InputManagerCompat.InputDeviceListener} in your main
608{@link android.view.View}. Because of the version-switching logic you set
609up, your game automatically uses the implementation that's appropriate for the
610version of Android the device is running.</p>
611<pre>
612public class GameView extends View implements InputDeviceListener {
613    private InputManagerCompat mInputManager;
614    ...
615
616    public GameView(Context context, AttributeSet attrs) {
617        mInputManager =
618                InputManagerCompat.Factory.getInputManager(this.getContext());
619        mInputManager.registerInputDeviceListener(this, null);
620        ...
621    }
622}
623</pre>
624<p>Next, override the
625{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
626onGenericMotionEvent()} method in your main view, as described in
627<a href="controller-input.html#analog">Handle a MotionEvent from a Game
628Controller</a>. Your game should now be able to process game controller events
629consistently on devices running Android 2.3 (API level 9) and higher.
630<p>
631<pre>
632&#64;Override
633public boolean onGenericMotionEvent(MotionEvent event) {
634    mInputManager.onGenericMotionEvent(event);
635
636    // Handle analog input from the controller as normal
637    ...
638    return super.onGenericMotionEvent(event);
639}
640</pre>
641<p>You can find a complete implementation of this compatibility code in the
642{@code GameView} class provided in the sample {@code ControllerSample.zip}
643available for download above.</p>