1 package com.badlogic.gdx.controllers.android;
\r
3 import java.util.List;
\r
5 import android.view.InputDevice;
\r
6 import android.view.InputDevice.MotionRange;
\r
7 import android.view.KeyEvent;
\r
8 import android.view.MotionEvent;
\r
9 import android.view.View;
\r
10 import android.view.View.OnGenericMotionListener;
\r
11 import android.view.View.OnKeyListener;
\r
13 import com.badlogic.gdx.Gdx;
\r
14 import com.badlogic.gdx.backends.android.AndroidInput;
\r
15 import com.badlogic.gdx.backends.android.AndroidInput.PauseResumeListener;
\r
16 import com.badlogic.gdx.backends.android.AndroidInputThreePlus;
\r
17 import com.badlogic.gdx.controllers.Controller;
\r
18 import com.badlogic.gdx.controllers.ControllerListener;
\r
19 import com.badlogic.gdx.controllers.ControllerManager;
\r
20 import com.badlogic.gdx.utils.Array;
\r
21 import com.badlogic.gdx.utils.IntMap;
\r
22 import com.badlogic.gdx.utils.IntMap.Entry;
\r
23 import com.badlogic.gdx.utils.Pool;
\r
25 public class AndroidControllers implements PauseResumeListener, ControllerManager, OnKeyListener, OnGenericMotionListener {
\r
26 private final static String TAG = "AndroidControllers";
\r
27 private final IntMap<AndroidController> controllerMap = new IntMap<AndroidController>();
\r
28 private final Array<Controller> controllers = new Array<Controller>();
\r
29 private final Array<ControllerListener> listeners = new Array<ControllerListener>();
\r
30 private final Array<AndroidControllerEvent> eventQueue = new Array<AndroidControllerEvent>();
\r
31 private final Pool<AndroidControllerEvent> eventPool = new Pool<AndroidControllerEvent>() {
\r
33 protected AndroidControllerEvent newObject () {
\r
34 return new AndroidControllerEvent();
\r
38 public AndroidControllers() {
\r
39 ((AndroidInput)Gdx.input).addPauseResumeListener(this);
\r
40 gatherControllers(false);
\r
42 ((AndroidInput)Gdx.input).addKeyListener(this);
\r
43 ((AndroidInputThreePlus)Gdx.input).addGenericMotionListener(this);
\r
45 // use InputManager on Android +4.1 to receive (dis-)connect events
\r
46 if(Gdx.app.getVersion() >= 16) {
\r
48 String className = "com.badlogic.gdx.controllers.android.ControllerLifeCycleListener";
\r
49 Class.forName(className).getConstructor(AndroidControllers.class).newInstance(this);
\r
50 } catch(Exception e) {
\r
51 Gdx.app.log(TAG, "Couldn't register controller life-cycle listener");
\r
56 private void setupEventQueue() {
\r
58 @SuppressWarnings("synthetic-access")
\r
60 public void run () {
\r
61 synchronized(eventQueue) {
\r
62 for(AndroidControllerEvent event: eventQueue) {
\r
63 switch(event.type) {
\r
64 case AndroidControllerEvent.CONNECTED:
\r
65 controllers.add(event.controller);
\r
66 for(ControllerListener listener: listeners) {
\r
67 listener.connected(event.controller);
\r
70 case AndroidControllerEvent.DISCONNECTED:
\r
71 controllers.removeValue(event.controller, true);
\r
72 for(ControllerListener listener: listeners) {
\r
73 listener.disconnected(event.controller);
\r
75 for(ControllerListener listener: event.controller.getListeners()) {
\r
76 listener.disconnected(event.controller);
\r
79 case AndroidControllerEvent.BUTTON_DOWN:
\r
80 event.controller.buttons.put(event.code, event.code);
\r
81 for(ControllerListener listener: listeners) {
\r
82 if(listener.buttonDown(event.controller, event.code)) break;
\r
84 for(ControllerListener listener: event.controller.getListeners()) {
\r
85 if(listener.buttonDown(event.controller, event.code)) break;
\r
88 case AndroidControllerEvent.BUTTON_UP:
\r
89 event.controller.buttons.remove(event.code, 0);
\r
90 for(ControllerListener listener: listeners) {
\r
91 if(listener.buttonUp(event.controller, event.code)) break;
\r
93 for(ControllerListener listener: event.controller.getListeners()) {
\r
94 if(listener.buttonUp(event.controller, event.code)) break;
\r
97 case AndroidControllerEvent.AXIS:
\r
98 event.controller.axes[event.code] = event.axisValue;
\r
99 for(ControllerListener listener: listeners) {
\r
100 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
102 for(ControllerListener listener: event.controller.getListeners()) {
\r
103 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
109 eventPool.freeAll(eventQueue);
\r
110 eventQueue.clear();
\r
112 Gdx.app.postRunnable(this);
\r
118 public boolean onGenericMotion (View view, MotionEvent motionEvent) {
\r
119 if((motionEvent.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) return false;
\r
120 AndroidController controller = controllerMap.get(motionEvent.getDeviceId());
\r
121 if(controller != null) {
\r
122 synchronized(eventQueue) {
\r
123 final int historySize = motionEvent.getHistorySize();
\r
125 for (int axisId: controller.axesIds) {
\r
126 float axisValue = motionEvent.getAxisValue(axisId);
\r
127 if(controller.getAxis(axisIndex) == axisValue) {
\r
128 Gdx.app.log(TAG, "skipped axis " + axisIndex + ", " + axisValue);
\r
132 AndroidControllerEvent event = eventPool.obtain();
\r
133 event.type = AndroidControllerEvent.AXIS;
\r
134 event.controller = controller;
\r
135 event.code = axisIndex;
\r
136 event.axisValue = axisValue;
\r
137 eventQueue.add(event);
\r
147 public boolean onKey (View view, int keyCode, KeyEvent keyEvent) {
\r
148 AndroidController controller = controllerMap.get(keyEvent.getDeviceId());
\r
149 if(controller != null) {
\r
150 if(keyEvent.getRepeatCount() == 0) {
\r
151 synchronized(eventQueue) {
\r
152 AndroidControllerEvent event = eventPool.obtain();
\r
153 event.controller = controller;
\r
154 if(keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
\r
155 event.type = AndroidControllerEvent.BUTTON_DOWN;
\r
157 event.type = AndroidControllerEvent.BUTTON_UP;
\r
159 event.code = keyCode;
\r
160 eventQueue.add(event);
\r
169 private void gatherControllers(boolean sendEvent) {
\r
170 // gather all joysticks and gamepads, remove any disconnected ones
\r
171 IntMap<AndroidController> removedControllers = new IntMap<AndroidController>();
\r
172 removedControllers.putAll(controllerMap);
\r
174 for(int deviceId: InputDevice.getDeviceIds()) {
\r
175 InputDevice device = InputDevice.getDevice(deviceId);
\r
176 AndroidController controller = controllerMap.get(deviceId);
\r
177 if(controller != null) {
\r
178 removedControllers.remove(deviceId);
\r
180 addController(deviceId, sendEvent);
\r
184 for(Entry<AndroidController> entry: removedControllers.entries()) {
\r
185 removeController(entry.key);
\r
189 protected void addController(int deviceId, boolean sendEvent) {
\r
190 InputDevice device = InputDevice.getDevice(deviceId);
\r
191 if(!isController(device)) return;
\r
192 String name = device.getName();
\r
193 AndroidController controller = new AndroidController(deviceId, name);
\r
194 controllerMap.put(deviceId, controller);
\r
196 synchronized(eventQueue) {
\r
197 AndroidControllerEvent event = eventPool.obtain();
\r
198 event.type = AndroidControllerEvent.CONNECTED;
\r
199 event.controller = controller;
\r
200 eventQueue.add(event);
\r
203 controllers.add(controller);
\r
205 Gdx.app.log(TAG, "added controller '" + name + "'");
\r
208 protected void removeController(int deviceId) {
\r
209 AndroidController controller = controllerMap.remove(deviceId);
\r
210 if(controller != null) {
\r
211 synchronized(eventQueue) {
\r
212 AndroidControllerEvent event = eventPool.obtain();
\r
213 event.type = AndroidControllerEvent.DISCONNECTED;
\r
214 event.controller = controller;
\r
215 eventQueue.add(event);
\r
217 Gdx.app.log(TAG, "removed controller '" + controller.getName() + "'");
\r
221 private boolean isController(InputDevice device) {
\r
222 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0;
\r
226 public Array<Controller> getControllers () {
\r
227 return controllers;
\r
231 public void addListener (ControllerListener listener) {
\r
232 synchronized(eventQueue) {
\r
233 listeners.add(listener);
\r
238 public void removeListener (ControllerListener listener) {
\r
239 synchronized(eventQueue) {
\r
240 listeners.removeValue(listener, true);
\r
245 public void pause () {
\r
246 Gdx.app.log(TAG, "controllers paused");
\r
250 public void resume () {
\r
251 gatherControllers(true);
\r
252 Gdx.app.log(TAG, "controllers resumed");
\r