1 package com.badlogic.gdx.controllers.android;
\r
3 import android.view.InputDevice;
\r
4 import android.view.KeyEvent;
\r
5 import android.view.MotionEvent;
\r
6 import android.view.View;
\r
7 import android.view.View.OnGenericMotionListener;
\r
8 import android.view.View.OnKeyListener;
\r
10 import com.badlogic.gdx.Gdx;
\r
11 import com.badlogic.gdx.LifecycleListener;
\r
12 import com.badlogic.gdx.backends.android.AndroidInput;
\r
13 import com.badlogic.gdx.backends.android.AndroidInputThreePlus;
\r
14 import com.badlogic.gdx.controllers.Controller;
\r
15 import com.badlogic.gdx.controllers.ControllerListener;
\r
16 import com.badlogic.gdx.controllers.ControllerManager;
\r
17 import com.badlogic.gdx.utils.Array;
\r
18 import com.badlogic.gdx.utils.IntMap;
\r
19 import com.badlogic.gdx.utils.IntMap.Entry;
\r
20 import com.badlogic.gdx.utils.Pool;
\r
22 public class AndroidControllers implements LifecycleListener, ControllerManager, OnKeyListener, OnGenericMotionListener {
\r
23 private final static String TAG = "AndroidControllers";
\r
24 private final IntMap<AndroidController> controllerMap = new IntMap<AndroidController>();
\r
25 private final Array<Controller> controllers = new Array<Controller>();
\r
26 private final Array<ControllerListener> listeners = new Array<ControllerListener>();
\r
27 private final Array<AndroidControllerEvent> eventQueue = new Array<AndroidControllerEvent>();
\r
28 private final Pool<AndroidControllerEvent> eventPool = new Pool<AndroidControllerEvent>() {
\r
30 protected AndroidControllerEvent newObject () {
\r
31 return new AndroidControllerEvent();
\r
35 public AndroidControllers() {
\r
36 Gdx.app.addLifecycleListener(this);
\r
37 gatherControllers(false);
\r
39 ((AndroidInput)Gdx.input).addKeyListener(this);
\r
40 ((AndroidInputThreePlus)Gdx.input).addGenericMotionListener(this);
\r
42 // use InputManager on Android +4.1 to receive (dis-)connect events
\r
43 if(Gdx.app.getVersion() >= 16) {
\r
45 String className = "com.badlogic.gdx.controllers.android.ControllerLifeCycleListener";
\r
46 Class.forName(className).getConstructor(AndroidControllers.class).newInstance(this);
\r
47 } catch(Exception e) {
\r
48 Gdx.app.log(TAG, "Couldn't register controller life-cycle listener");
\r
53 private void setupEventQueue() {
\r
55 @SuppressWarnings("synthetic-access")
\r
57 public void run () {
\r
58 synchronized(eventQueue) {
\r
59 for(AndroidControllerEvent event: eventQueue) {
\r
60 switch(event.type) {
\r
61 case AndroidControllerEvent.CONNECTED:
\r
62 controllers.add(event.controller);
\r
63 for(ControllerListener listener: listeners) {
\r
64 listener.connected(event.controller);
\r
67 case AndroidControllerEvent.DISCONNECTED:
\r
68 controllers.removeValue(event.controller, true);
\r
69 for(ControllerListener listener: listeners) {
\r
70 listener.disconnected(event.controller);
\r
72 for(ControllerListener listener: event.controller.getListeners()) {
\r
73 listener.disconnected(event.controller);
\r
76 case AndroidControllerEvent.BUTTON_DOWN:
\r
77 event.controller.buttons.put(event.code, event.code);
\r
78 for(ControllerListener listener: listeners) {
\r
79 if(listener.buttonDown(event.controller, event.code)) break;
\r
81 for(ControllerListener listener: event.controller.getListeners()) {
\r
82 if(listener.buttonDown(event.controller, event.code)) break;
\r
85 case AndroidControllerEvent.BUTTON_UP:
\r
86 event.controller.buttons.remove(event.code, 0);
\r
87 for(ControllerListener listener: listeners) {
\r
88 if(listener.buttonUp(event.controller, event.code)) break;
\r
90 for(ControllerListener listener: event.controller.getListeners()) {
\r
91 if(listener.buttonUp(event.controller, event.code)) break;
\r
94 case AndroidControllerEvent.AXIS:
\r
95 event.controller.axes[event.code] = event.axisValue;
\r
96 for(ControllerListener listener: listeners) {
\r
97 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
99 for(ControllerListener listener: event.controller.getListeners()) {
\r
100 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
106 eventPool.freeAll(eventQueue);
\r
107 eventQueue.clear();
\r
109 Gdx.app.postRunnable(this);
\r
115 public boolean onGenericMotion (View view, MotionEvent motionEvent) {
\r
116 if((motionEvent.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) return false;
\r
117 AndroidController controller = controllerMap.get(motionEvent.getDeviceId());
\r
118 if(controller != null) {
\r
119 synchronized(eventQueue) {
\r
120 final int historySize = motionEvent.getHistorySize();
\r
122 for (int axisId: controller.axesIds) {
\r
123 float axisValue = motionEvent.getAxisValue(axisId);
\r
124 if(controller.getAxis(axisIndex) == axisValue) {
\r
128 AndroidControllerEvent event = eventPool.obtain();
\r
129 event.type = AndroidControllerEvent.AXIS;
\r
130 event.controller = controller;
\r
131 event.code = axisIndex;
\r
132 event.axisValue = axisValue;
\r
133 eventQueue.add(event);
\r
143 public boolean onKey (View view, int keyCode, KeyEvent keyEvent) {
\r
144 AndroidController controller = controllerMap.get(keyEvent.getDeviceId());
\r
145 if(controller != null) {
\r
146 if(keyEvent.getRepeatCount() == 0) {
\r
147 synchronized(eventQueue) {
\r
148 AndroidControllerEvent event = eventPool.obtain();
\r
149 event.controller = controller;
\r
150 if(keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
\r
151 event.type = AndroidControllerEvent.BUTTON_DOWN;
\r
153 event.type = AndroidControllerEvent.BUTTON_UP;
\r
155 event.code = keyCode;
\r
156 eventQueue.add(event);
\r
165 private void gatherControllers(boolean sendEvent) {
\r
166 // gather all joysticks and gamepads, remove any disconnected ones
\r
167 IntMap<AndroidController> removedControllers = new IntMap<AndroidController>();
\r
168 removedControllers.putAll(controllerMap);
\r
170 for(int deviceId: InputDevice.getDeviceIds()) {
\r
171 InputDevice device = InputDevice.getDevice(deviceId);
\r
172 AndroidController controller = controllerMap.get(deviceId);
\r
173 if(controller != null) {
\r
174 removedControllers.remove(deviceId);
\r
176 addController(deviceId, sendEvent);
\r
180 for(Entry<AndroidController> entry: removedControllers.entries()) {
\r
181 removeController(entry.key);
\r
185 protected void addController(int deviceId, boolean sendEvent) {
\r
186 InputDevice device = InputDevice.getDevice(deviceId);
\r
187 if(!isController(device)) return;
\r
188 String name = device.getName();
\r
189 AndroidController controller = new AndroidController(deviceId, name);
\r
190 controllerMap.put(deviceId, controller);
\r
192 synchronized(eventQueue) {
\r
193 AndroidControllerEvent event = eventPool.obtain();
\r
194 event.type = AndroidControllerEvent.CONNECTED;
\r
195 event.controller = controller;
\r
196 eventQueue.add(event);
\r
199 controllers.add(controller);
\r
201 Gdx.app.log(TAG, "added controller '" + name + "'");
\r
204 protected void removeController(int deviceId) {
\r
205 AndroidController controller = controllerMap.remove(deviceId);
\r
206 if(controller != null) {
\r
207 synchronized(eventQueue) {
\r
208 AndroidControllerEvent event = eventPool.obtain();
\r
209 event.type = AndroidControllerEvent.DISCONNECTED;
\r
210 event.controller = controller;
\r
211 eventQueue.add(event);
\r
213 Gdx.app.log(TAG, "removed controller '" + controller.getName() + "'");
\r
217 private boolean isController(InputDevice device) {
\r
218 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0;
\r
222 public Array<Controller> getControllers () {
\r
223 return controllers;
\r
227 public void addListener (ControllerListener listener) {
\r
228 synchronized(eventQueue) {
\r
229 listeners.add(listener);
\r
234 public void removeListener (ControllerListener listener) {
\r
235 synchronized(eventQueue) {
\r
236 listeners.removeValue(listener, true);
\r
241 public void pause () {
\r
242 Gdx.app.log(TAG, "controllers paused");
\r
246 public void resume () {
\r
247 gatherControllers(true);
\r
248 Gdx.app.log(TAG, "controllers resumed");
\r
252 public void dispose () {
\r