Assurer la compatibilité avec des manettes de jeu dans différentes versions d'Android

Si votre jeu est compatible avec les manettes de jeu, il vous incombe de vous assurer qu'il y répond de manière cohérente sur les appareils exécutant différentes versions d'Android. Cela permet à votre jeu de toucher un public plus large, et vos joueurs peuvent profiter d'une expérience de jeu fluide avec leurs manettes, même lorsqu'ils changent d'appareil Android ou en mettent un à niveau.

Cette leçon montre comment utiliser les API disponibles dans Android 4.1 et versions ultérieures de manière rétrocompatible, ce qui permet à votre jeu de prendre en charge les fonctionnalités suivantes sur les appareils équipés d'Android 3.1 et versions ultérieures :

  • Le jeu peut détecter si une nouvelle manette de jeu est ajoutée, modifiée ou supprimée.
  • Le jeu peut interroger les fonctionnalités d'une manette de jeu.
  • Le jeu peut reconnaître les événements de mouvement entrants provenant d'une manette de jeu.

Se préparer à éliminer les API pour la prise en charge des manettes de jeu

Supposons que vous souhaitiez pouvoir déterminer si l'état de connexion d'une manette de jeu a changé sur les appareils exécutant Android 3.1 (niveau d'API 12). Toutefois, les API ne sont disponibles que dans Android 4.1 (niveau d'API 16) et versions ultérieures. Vous devez donc fournir une implémentation compatible avec Android 4.1 et versions ultérieures, tout en fournissant un mécanisme de secours compatible avec Android 3.1 à Android 4.0.

Pour vous aider à déterminer quelles fonctionnalités nécessitent un mécanisme de secours pour les versions antérieures, le tableau 1 liste les différences de compatibilité avec les manettes de jeu entre Android 3.1 (niveau d'API 12) et Android 4.1 (niveau d'API 16).

Tableau 1. API pour la compatibilité avec les manettes de jeu dans différentes versions d'Android.

Informations sur la manette API Controller Niveau d'API 12 Niveau d'API 16
Identification de l'appareil getInputDeviceIds()  
getInputDevice()  
getVibrator()  
SOURCE_JOYSTICK
SOURCE_GAMEPAD
État de la connexion onInputDeviceAdded()  
onInputDeviceChanged()  
onInputDeviceRemoved()  
Identification des événements d'entrée Appui sur le pavé directionnel ( KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_CENTER)
Appui sur un bouton de la manette de jeu ( BUTTON_A, BUTTON_B, BUTTON_THUMBL, BUTTON_THUMBR, BUTTON_SELECT, BUTTON_START, BUTTON_R1, BUTTON_L1, BUTTON_R2, BUTTON_L2)
Mouvement du joystick et du chapeau (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RZ, AXIS_HAT_X, AXIS_HAT_Y)
Pression sur le bouton de déclenchement analogique ( AXIS_LTRIGGER, AXIS_RTRIGGER)

Vous pouvez utiliser l'abstraction pour créer une compatibilité avec les manettes de jeu qui tient compte des versions et qui fonctionne sur toutes les plates-formes. Cette approche comprend les étapes suivantes :

  1. Définissez une interface Java intermédiaire qui résume l'implémentation des fonctionnalités de la manette de jeu requises par votre jeu.
  2. Créez une implémentation de proxy de votre interface qui utilise les API dans Android 4.1 et versions ultérieures.
  3. Créez une implémentation personnalisée de votre interface qui utilise les API disponibles entre Android 3.1 et Android 4.0.
  4. Créez la logique permettant de basculer entre ces implémentations au moment de l'exécution, puis commencez à utiliser l'interface dans votre jeu.

Pour obtenir un aperçu de la façon dont l'abstraction peut être utilisée pour vérifier que les applications peuvent fonctionner de manière rétrocompatible sur différentes versions d'Android, consultez Créer des UI rétrocompatibles.

Ajouter une interface pour la rétrocompatibilité

Pour assurer la rétrocompatibilité, vous pouvez créer une interface personnalisée, puis ajouter des implémentations spécifiques à la version. L'un des avantages de cette approche est qu'elle vous permet de refléter les interfaces publiques sur Android 4.1 (niveau d'API 16) qui prennent en charge les manettes de jeu.

Kotlin

// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
interface InputManagerCompat {
    val inputDeviceIds: IntArray
    fun getInputDevice(id: Int): InputDevice

    fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    )

    fun unregisterInputDeviceListener(listener:InputManager.InputDeviceListener)

    fun onGenericMotionEvent(event: MotionEvent)

    fun onPause()
    fun onResume()

    interface InputDeviceListener {
        fun onInputDeviceAdded(deviceId: Int)
        fun onInputDeviceChanged(deviceId: Int)
        fun onInputDeviceRemoved(deviceId: Int)
    }
}

Java

// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
public interface InputManagerCompat {
    ...
    public InputDevice getInputDevice(int id);
    public int[] getInputDeviceIds();

    public void registerInputDeviceListener(
            InputManagerCompat.InputDeviceListener listener,
            Handler handler);
    public void unregisterInputDeviceListener(
            InputManagerCompat.InputDeviceListener listener);

    public void onGenericMotionEvent(MotionEvent event);

    public void onPause();
    public void onResume();

    public interface InputDeviceListener {
        void onInputDeviceAdded(int deviceId);
        void onInputDeviceChanged(int deviceId);
        void onInputDeviceRemoved(int deviceId);
    }
    ...
}

L'interface InputManagerCompat fournit les méthodes suivantes :

getInputDevice()
MiroirsgetInputDevice() : Obtient l'objet InputDevice qui représente les fonctionnalités d'une manette de jeu.
getInputDeviceIds()
MiroirsgetInputDeviceIds() : Renvoie un tableau d'entiers, chacun étant un ID pour un périphérique d'entrée différent. Cela est utile si vous développez un jeu multijoueur et que vous souhaitez détecter le nombre de manettes connectées.
registerInputDeviceListener()
MiroirsregisterInputDeviceListener() : Vous permet de vous inscrire pour être informé lorsqu'un nouvel appareil est ajouté, modifié ou supprimé.
unregisterInputDeviceListener()
MiroirsunregisterInputDeviceListener() : Désenregistre un écouteur de périphérique d'entrée.
onGenericMotionEvent()
MiroirsonGenericMotionEvent() : Permet à votre jeu d'intercepter et de gérer les objets MotionEvent et les valeurs d'axe qui représentent des événements tels que les mouvements du joystick et les pressions sur les gâchettes analogiques.
onPause()
Arrête l'interrogation des événements de la manette de jeu lorsque l'activité principale est mise en pause ou lorsque le jeu n'est plus au premier plan.
onResume()
Commence à interroger les événements de la manette de jeu lorsque l'activité principale est reprise ou lorsque le jeu est lancé et s'exécute au premier plan.
InputDeviceListener
reflète l'interface InputManager.InputDeviceListener. Indique à votre jeu lorsqu'une manette de jeu a été ajoutée, modifiée ou supprimée.

Créez ensuite des implémentations pour InputManagerCompat qui fonctionnent sur différentes versions de plate-forme. Si votre jeu s'exécute sur Android 4.1 ou version ultérieure et appelle une méthode InputManagerCompat, l'implémentation du proxy appelle la méthode équivalente dans InputManager. Toutefois, si votre jeu s'exécute sur Android 3.1 à Android 4.0, l'implémentation personnalisée traite les appels aux méthodes InputManagerCompat en utilisant uniquement les API introduites au plus tard dans Android 3.1. Quelle que soit l'implémentation spécifique à la version utilisée lors de l'exécution, l'implémentation transmet les résultats de l'appel de manière transparente au jeu.

Diagramme de classe de l'interface et des implémentations spécifiques à la version.
Figure 1 : Diagramme de classe de l'interface et des implémentations spécifiques à la version.

Implémenter l'interface sur Android 4.1 ou version ultérieure

InputManagerCompatV16 est une implémentation de l'interface InputManagerCompat qui transmet les appels de méthode à un InputManager et un InputManager.InputDeviceListener réels. Le InputManager est obtenu à partir du Context du système.

Kotlin

// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16(
        context: Context,
        private val inputManager: InputManager =
            context.getSystemService(Context.INPUT_SERVICE) as InputManager,
        private val listeners:
            MutableMap<InputManager.InputDeviceListener, V16InputDeviceListener> = mutableMapOf()
) : InputManagerCompat {
    override val inputDeviceIds: IntArray = inputManager.inputDeviceIds

    override fun getInputDevice(id: Int): InputDevice = inputManager.getInputDevice(id)

    override fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    ) {
        V16InputDeviceListener(listener).also { v16listener ->
            inputManager.registerInputDeviceListener(v16listener, handler)
            listeners += listener to v16listener
        }
    }

    // Do the same for unregistering an input device listener
    ...

    override fun onGenericMotionEvent(event: MotionEvent) {
        // unused in V16
    }

    override fun onPause() {
        // unused in V16
    }

    override fun onResume() {
        // unused in V16
    }

}

class V16InputDeviceListener(
        private val idl: InputManager.InputDeviceListener
) : InputManager.InputDeviceListener {

    override fun onInputDeviceAdded(deviceId: Int) {
        idl.onInputDeviceAdded(deviceId)
    }
    // Do the same for device change and removal
    ...
}

Java

// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16 implements InputManagerCompat {

    private final InputManager inputManager;
    private final Map<InputManagerCompat.InputDeviceListener,
            V16InputDeviceListener> listeners;

    public InputManagerV16(Context context) {
        inputManager = (InputManager)
                context.getSystemService(Context.INPUT_SERVICE);
        listeners = new HashMap<InputManagerCompat.InputDeviceListener,
                V16InputDeviceListener>();
    }

    @Override
    public InputDevice getInputDevice(int id) {
        return inputManager.getInputDevice(id);
    }

    @Override
    public int[] getInputDeviceIds() {
        return inputManager.getInputDeviceIds();
    }

    static class V16InputDeviceListener implements
            InputManager.InputDeviceListener {
        final InputManagerCompat.InputDeviceListener mIDL;

        public V16InputDeviceListener(InputDeviceListener idl) {
            mIDL = idl;
        }

        @Override
        public void onInputDeviceAdded(int deviceId) {
            mIDL.onInputDeviceAdded(deviceId);
        }

        // Do the same for device change and removal
        ...
    }

    @Override
    public void registerInputDeviceListener(InputDeviceListener listener,
            Handler handler) {
        V16InputDeviceListener v16Listener = new
                V16InputDeviceListener(listener);
        inputManager.registerInputDeviceListener(v16Listener, handler);
        listeners.put(listener, v16Listener);
    }

    // Do the same for unregistering an input device listener
    ...

    @Override
    public void onGenericMotionEvent(MotionEvent event) {
        // unused in V16
    }

    @Override
    public void onPause() {
        // unused in V16
    }

    @Override
    public void onResume() {
        // unused in V16
    }

}

Implémenter l'interface sur Android 3.1 à 4.0

Pour créer une implémentation de InputManagerCompat compatible avec Android 3.1 à Android 4.0, vous pouvez utiliser les objets suivants :

  • SparseArray d'ID d'appareil pour suivre les manettes de jeu connectées à l'appareil.
  • Un Handler pour traiter les événements de l'appareil. Lorsqu'une application est lancée ou reprise, Handler reçoit un message pour commencer à interroger la manette de jeu afin de détecter une éventuelle déconnexion. Handler lancera une boucle pour vérifier chaque manette de jeu connectée connue et voir si un ID d'appareil est renvoyé. Une valeur de retour null indique que la manette de jeu est déconnectée. Handler arrête l'interrogation lorsque l'application est suspendue.
  • Map d'objets InputManagerCompat.InputDeviceListener. Vous utiliserez les écouteurs pour mettre à jour l'état de connexion des manettes de jeu suivies.

Kotlin

// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    private val defaultHandler: Handler = PollingMessageHandler(this)
    
}

Java

// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV9 implements InputManagerCompat {
    private final SparseArray<long[]> devices;
    private final Map<InputDeviceListener, Handler> listeners;
    private final Handler defaultHandler;
    

    public InputManagerV9() {
        devices = new SparseArray<long[]>();
        listeners = new HashMap<InputDeviceListener, Handler>();
        defaultHandler = new PollingMessageHandler(this);
    }
}

Implémentez un objet PollingMessageHandler qui étend Handler et remplace la méthode handleMessage(). Cette méthode vérifie si une manette de jeu connectée a été déconnectée et en informe les écouteurs enregistrés.

Kotlin

private class PollingMessageHandler(
        inputManager: InputManagerV9,
        private val mInputManager: WeakReference<InputManagerV9> = WeakReference(inputManager)
) : Handler() {

    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        when (msg.what) {
            MESSAGE_TEST_FOR_DISCONNECT -> {
                mInputManager.get()?.also { imv ->
                    val time = SystemClock.elapsedRealtime()
                    val size = imv.devices.size()
                    for (i in 0 until size) {
                        imv.devices.valueAt(i)?.also { lastContact ->
                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
                                // check to see if the device has been
                                // disconnected
                                val id = imv.devices.keyAt(i)
                                if (null == InputDevice.getDevice(id)) {
                                    // Notify the registered listeners
                                    // that the game controller is disconnected
                                    imv.devices.remove(id)
                                } else {
                                    lastContact[0] = time
                                }
                            }
                        }
                    }
                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
                }
            }
        }
    }
}

Java

private static class PollingMessageHandler extends Handler {
    private final WeakReference<InputManagerV9> inputManager;

    PollingMessageHandler(InputManagerV9 im) {
        inputManager = new WeakReference<InputManagerV9>(im);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case MESSAGE_TEST_FOR_DISCONNECT:
                InputManagerV9 imv = inputManager.get();
                if (null != imv) {
                    long time = SystemClock.elapsedRealtime();
                    int size = imv.devices.size();
                    for (int i = 0; i < size; i++) {
                        long[] lastContact = imv.devices.valueAt(i);
                        if (null != lastContact) {
                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
                                // check to see if the device has been
                                // disconnected
                                int id = imv.devices.keyAt(i);
                                if (null == InputDevice.getDevice(id)) {
                                    // Notify the registered listeners
                                    // that the game controller is disconnected
                                    imv.devices.remove(id);
                                } else {
                                    lastContact[0] = time;
                                }
                            }
                        }
                    }
                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
                            CHECK_ELAPSED_TIME);
                }
                break;
        }
    }
}

Pour démarrer et arrêter l'interrogation de la déconnexion de la manette de jeu, remplacez ces méthodes :

Kotlin

private const val MESSAGE_TEST_FOR_DISCONNECT = 101
private const val CHECK_ELAPSED_TIME = 3000L

class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    ...
    override fun onPause() {
        defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT)
    }

    override fun onResume() {
        defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
    }
    ...
}

Java

private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
private static final long CHECK_ELAPSED_TIME = 3000L;

@Override
public void onPause() {
    defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
}

@Override
public void onResume() {
    defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
            CHECK_ELAPSED_TIME);
}

Pour détecter qu'un périphérique d'entrée a été ajouté, remplacez la méthode onGenericMotionEvent(). Lorsque le système signale un événement de mouvement, vérifiez si cet événement provient d'un ID d'appareil déjà suivi ou d'un nouvel ID d'appareil. Si l'ID de l'appareil est nouveau, avertissez les auditeurs enregistrés.

Kotlin

override fun onGenericMotionEvent(event: MotionEvent) {
    // detect new devices
    val id = event.deviceId
    val timeArray: Array<Long> = mDevices.get(id) ?: run {
        // Notify the registered listeners that a game controller is added
        ...
        arrayOf<Long>().also {
            mDevices.put(id, it)
        }
    }
    timeArray[0] = SystemClock.elapsedRealtime()
}

Java

@Override
public void onGenericMotionEvent(MotionEvent event) {
    // detect new devices
    int id = event.getDeviceId();
    long[] timeArray = mDevices.get(id);
    if (null == timeArray) {
        // Notify the registered listeners that a game controller is added
        ...
        timeArray = new long[1];
        mDevices.put(id, timeArray);
    }
    long time = SystemClock.elapsedRealtime();
    timeArray[0] = time;
}

La notification des écouteurs est implémentée à l'aide de l'objet Handler pour envoyer un objet DeviceEvent Runnable à la file d'attente des messages. Le DeviceEvent contient une référence à un InputManagerCompat.InputDeviceListener. Lorsque DeviceEvent s'exécute, la méthode de rappel appropriée de l'écouteur est appelée pour signaler si la manette de jeu a été ajoutée, modifiée ou supprimée.

Kotlin

class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    ...
    override fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    ) {
        listeners[listener] = handler ?: defaultHandler
    }

    override fun unregisterInputDeviceListener(listener: InputManager.InputDeviceListener) {
        listeners.remove(listener)
    }

    private fun notifyListeners(why: Int, deviceId: Int) {
        // the state of some device has changed
        listeners.forEach { listener, handler ->
            DeviceEvent.getDeviceEvent(why, deviceId, listener).also {
                handler?.post(it)
            }
        }
    }
    ...
}

private val sObjectQueue: Queue<DeviceEvent> = ArrayDeque<DeviceEvent>()

private class DeviceEvent(
        private var mMessageType: Int,
        private var mId: Int,
        private var mListener: InputManager.InputDeviceListener
) : Runnable {

    companion object {
        fun getDeviceEvent(messageType: Int, id: Int, listener: InputManager.InputDeviceListener) =
                sObjectQueue.poll()?.apply {
                    mMessageType = messageType
                    mId = id
                    mListener = listener
                } ?: DeviceEvent(messageType, id, listener)

    }

    override fun run() {
        when(mMessageType) {
            ON_DEVICE_ADDED -> mListener.onInputDeviceAdded(mId)
            ON_DEVICE_CHANGED -> mListener.onInputDeviceChanged(mId)
            ON_DEVICE_REMOVED -> mListener.onInputDeviceChanged(mId)
            else -> {
                // Handle unknown message type
            }
        }
    }

}

Java

@Override
public void registerInputDeviceListener(InputDeviceListener listener,
        Handler handler) {
    listeners.remove(listener);
    if (handler == null) {
        handler = defaultHandler;
    }
    listeners.put(listener, handler);
}

@Override
public void unregisterInputDeviceListener(InputDeviceListener listener) {
    listeners.remove(listener);
}

private void notifyListeners(int why, int deviceId) {
    // the state of some device has changed
    if (!listeners.isEmpty()) {
        for (InputDeviceListener listener : listeners.keySet()) {
            Handler handler = listeners.get(listener);
            DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
                    listener);
            handler.post(odc);
        }
    }
}

private static class DeviceEvent implements Runnable {
    private int mMessageType;
    private int mId;
    private InputDeviceListener mListener;
    private static Queue<DeviceEvent> sObjectQueue =
            new ArrayDeque<DeviceEvent>();
    ...

    static DeviceEvent getDeviceEvent(int messageType, int id,
            InputDeviceListener listener) {
        DeviceEvent curChanged = sObjectQueue.poll();
        if (null == curChanged) {
            curChanged = new DeviceEvent();
        }
        curChanged.mMessageType = messageType;
        curChanged.mId = id;
        curChanged.mListener = listener;
        return curChanged;
    }

    @Override
    public void run() {
        switch (mMessageType) {
            case ON_DEVICE_ADDED:
                mListener.onInputDeviceAdded(mId);
                break;
            case ON_DEVICE_CHANGED:
                mListener.onInputDeviceChanged(mId);
                break;
            case ON_DEVICE_REMOVED:
                mListener.onInputDeviceRemoved(mId);
                break;
            default:
                // Handle unknown message type
                ...
                break;
        }
        // Put this runnable back in the queue
        sObjectQueue.offer(this);
    }
}

Vous disposez désormais de deux implémentations de InputManagerCompat : l'une fonctionne sur les appareils équipés d'Android 4.1 ou version ultérieure, et l'autre sur les appareils équipés d'Android 3.1 à Android 4.0.

Utiliser l'implémentation spécifique à la version

La logique de commutation spécifique à la version est implémentée dans une classe qui sert de fabrique.

Kotlin

object Factory {
    fun getInputManager(context: Context): InputManagerCompat =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                InputManagerV16(context)
            } else {
                InputManagerV9()
            }
}

Java

public static class Factory {
    public static InputManagerCompat getInputManager(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            return new InputManagerV16(context);
        } else {
            return new InputManagerV9();
        }
    }
}

Vous pouvez maintenant instancier un objet InputManagerCompat et enregistrer un InputManagerCompat.InputDeviceListener dans votre View principal. Grâce à la logique de changement de version que vous avez configurée, votre jeu utilise automatiquement l'implémentation appropriée pour la version d'Android exécutée par l'appareil.

Kotlin

class GameView(context: Context) : View(context), InputManager.InputDeviceListener {
    private val inputManager: InputManagerCompat = Factory.getInputManager(context).apply {
        registerInputDeviceListener(this@GameView, null)
        ...
    }
    ...
}

Java

public class GameView extends View implements InputDeviceListener {
    private InputManagerCompat inputManager;
    ...

    public GameView(Context context, AttributeSet attrs) {
        inputManager =
                InputManagerCompat.Factory.getInputManager(this.getContext());
        inputManager.registerInputDeviceListener(this, null);
        ...
    }
}

Ensuite, remplacez la méthode onGenericMotionEvent() dans votre vue principale, comme décrit dans Gérer un MotionEvent à partir d'une manette de jeu et versions ultérieures. Votre jeu devrait désormais être en mesure de traiter les événements de la manette de jeu de manière cohérente sur les appareils exécutant Android 3.1 (niveau d'API).

Kotlin

override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    inputManager.onGenericMotionEvent(event)

    // Handle analog input from the controller as normal
    ...
    return super.onGenericMotionEvent(event)
}

Java

@Override
public boolean onGenericMotionEvent(MotionEvent event) {
    inputManager.onGenericMotionEvent(event);

    // Handle analog input from the controller as normal
    ...
    return super.onGenericMotionEvent(event);
}

Vous trouverez une implémentation complète de ce code de compatibilité dans la classe GameView fournie dans l'exemple ControllerSample.zip disponible au téléchargement.