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