1 /*******************************************************************************
\r
2 * Copyright 2011 See AUTHORS file.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
15 ******************************************************************************/
17 package com.badlogic.gdx.controllers.android;
\r
19 import android.view.InputDevice;
\r
20 import android.view.KeyEvent;
\r
21 import android.view.MotionEvent;
\r
22 import android.view.View;
\r
23 import android.view.View.OnGenericMotionListener;
\r
24 import android.view.View.OnKeyListener;
\r
26 import com.badlogic.gdx.Gdx;
\r
27 import com.badlogic.gdx.LifecycleListener;
\r
28 import com.badlogic.gdx.backends.android.AndroidInput;
\r
29 import com.badlogic.gdx.backends.android.AndroidInputThreePlus;
\r
30 import com.badlogic.gdx.controllers.Controller;
\r
31 import com.badlogic.gdx.controllers.ControllerListener;
\r
32 import com.badlogic.gdx.controllers.ControllerManager;
\r
33 import com.badlogic.gdx.utils.Array;
\r
34 import com.badlogic.gdx.utils.IntMap;
\r
35 import com.badlogic.gdx.utils.IntMap.Entry;
\r
36 import com.badlogic.gdx.utils.Pool;
\r
38 public class AndroidControllers implements LifecycleListener, ControllerManager, OnKeyListener, OnGenericMotionListener {
\r
39 private final static String TAG = "AndroidControllers";
\r
40 private final IntMap<AndroidController> controllerMap = new IntMap<AndroidController>();
\r
41 private final Array<Controller> controllers = new Array<Controller>();
\r
42 private final Array<ControllerListener> listeners = new Array<ControllerListener>();
\r
43 private final Array<AndroidControllerEvent> eventQueue = new Array<AndroidControllerEvent>();
\r
44 private final Pool<AndroidControllerEvent> eventPool = new Pool<AndroidControllerEvent>() {
\r
46 protected AndroidControllerEvent newObject () {
\r
47 return new AndroidControllerEvent();
\r
51 public AndroidControllers() {
\r
52 Gdx.app.addLifecycleListener(this);
\r
53 gatherControllers(false);
\r
55 ((AndroidInput)Gdx.input).addKeyListener(this);
\r
56 ((AndroidInputThreePlus)Gdx.input).addGenericMotionListener(this);
\r
58 // use InputManager on Android +4.1 to receive (dis-)connect events
\r
59 if(Gdx.app.getVersion() >= 16) {
\r
61 String className = "com.badlogic.gdx.controllers.android.ControllerLifeCycleListener";
\r
62 Class.forName(className).getConstructor(AndroidControllers.class).newInstance(this);
\r
63 } catch(Exception e) {
\r
64 Gdx.app.log(TAG, "Couldn't register controller life-cycle listener");
\r
69 private void setupEventQueue() {
\r
71 @SuppressWarnings("synthetic-access")
\r
73 public void run () {
\r
74 synchronized(eventQueue) {
\r
75 for(AndroidControllerEvent event: eventQueue) {
\r
76 switch(event.type) {
\r
77 case AndroidControllerEvent.CONNECTED:
\r
78 controllers.add(event.controller);
\r
79 for(ControllerListener listener: listeners) {
\r
80 listener.connected(event.controller);
\r
83 case AndroidControllerEvent.DISCONNECTED:
\r
84 controllers.removeValue(event.controller, true);
\r
85 for(ControllerListener listener: listeners) {
\r
86 listener.disconnected(event.controller);
\r
88 for(ControllerListener listener: event.controller.getListeners()) {
\r
89 listener.disconnected(event.controller);
\r
92 case AndroidControllerEvent.BUTTON_DOWN:
\r
93 event.controller.buttons.put(event.code, event.code);
\r
94 for(ControllerListener listener: listeners) {
\r
95 if(listener.buttonDown(event.controller, event.code)) break;
\r
97 for(ControllerListener listener: event.controller.getListeners()) {
\r
98 if(listener.buttonDown(event.controller, event.code)) break;
\r
101 case AndroidControllerEvent.BUTTON_UP:
\r
102 event.controller.buttons.remove(event.code, 0);
\r
103 for(ControllerListener listener: listeners) {
\r
104 if(listener.buttonUp(event.controller, event.code)) break;
\r
106 for(ControllerListener listener: event.controller.getListeners()) {
\r
107 if(listener.buttonUp(event.controller, event.code)) break;
\r
110 case AndroidControllerEvent.AXIS:
\r
111 event.controller.axes[event.code] = event.axisValue;
\r
112 for(ControllerListener listener: listeners) {
\r
113 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
115 for(ControllerListener listener: event.controller.getListeners()) {
\r
116 if(listener.axisMoved(event.controller, event.code, event.axisValue)) break;
\r
122 eventPool.freeAll(eventQueue);
\r
123 eventQueue.clear();
\r
125 Gdx.app.postRunnable(this);
\r
131 public boolean onGenericMotion (View view, MotionEvent motionEvent) {
\r
132 if((motionEvent.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) return false;
\r
133 AndroidController controller = controllerMap.get(motionEvent.getDeviceId());
\r
134 if(controller != null) {
\r
135 synchronized(eventQueue) {
\r
136 final int historySize = motionEvent.getHistorySize();
\r
138 for (int axisId: controller.axesIds) {
\r
139 float axisValue = motionEvent.getAxisValue(axisId);
\r
140 if(controller.getAxis(axisIndex) == axisValue) {
\r
144 AndroidControllerEvent event = eventPool.obtain();
\r
145 event.type = AndroidControllerEvent.AXIS;
\r
146 event.controller = controller;
\r
147 event.code = axisIndex;
\r
148 event.axisValue = axisValue;
\r
149 eventQueue.add(event);
\r
159 public boolean onKey (View view, int keyCode, KeyEvent keyEvent) {
\r
160 AndroidController controller = controllerMap.get(keyEvent.getDeviceId());
\r
161 if(controller != null) {
\r
162 if(controller.getButton(keyCode) && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
\r
165 synchronized(eventQueue) {
\r
166 AndroidControllerEvent event = eventPool.obtain();
\r
167 event.controller = controller;
\r
168 if(keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
\r
169 event.type = AndroidControllerEvent.BUTTON_DOWN;
\r
171 event.type = AndroidControllerEvent.BUTTON_UP;
\r
173 event.code = keyCode;
\r
174 eventQueue.add(event);
\r
182 private void gatherControllers(boolean sendEvent) {
\r
183 // gather all joysticks and gamepads, remove any disconnected ones
\r
184 IntMap<AndroidController> removedControllers = new IntMap<AndroidController>();
\r
185 removedControllers.putAll(controllerMap);
\r
187 for(int deviceId: InputDevice.getDeviceIds()) {
\r
188 InputDevice device = InputDevice.getDevice(deviceId);
\r
189 AndroidController controller = controllerMap.get(deviceId);
\r
190 if(controller != null) {
\r
191 removedControllers.remove(deviceId);
\r
193 addController(deviceId, sendEvent);
\r
197 for(Entry<AndroidController> entry: removedControllers.entries()) {
\r
198 removeController(entry.key);
\r
202 protected void addController(int deviceId, boolean sendEvent) {
\r
203 InputDevice device = InputDevice.getDevice(deviceId);
\r
204 if(!isController(device)) return;
\r
205 String name = device.getName();
\r
206 AndroidController controller = new AndroidController(deviceId, name);
\r
207 controllerMap.put(deviceId, controller);
\r
209 synchronized(eventQueue) {
\r
210 AndroidControllerEvent event = eventPool.obtain();
\r
211 event.type = AndroidControllerEvent.CONNECTED;
\r
212 event.controller = controller;
\r
213 eventQueue.add(event);
\r
216 controllers.add(controller);
\r
218 Gdx.app.log(TAG, "added controller '" + name + "'");
\r
221 protected void removeController(int deviceId) {
\r
222 AndroidController controller = controllerMap.remove(deviceId);
\r
223 if(controller != null) {
\r
224 synchronized(eventQueue) {
\r
225 AndroidControllerEvent event = eventPool.obtain();
\r
226 event.type = AndroidControllerEvent.DISCONNECTED;
\r
227 event.controller = controller;
\r
228 eventQueue.add(event);
\r
230 Gdx.app.log(TAG, "removed controller '" + controller.getName() + "'");
\r
234 private boolean isController(InputDevice device) {
\r
235 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0;
\r
239 public Array<Controller> getControllers () {
\r
240 return controllers;
\r
244 public void addListener (ControllerListener listener) {
\r
245 synchronized(eventQueue) {
\r
246 listeners.add(listener);
\r
251 public void removeListener (ControllerListener listener) {
\r
252 synchronized(eventQueue) {
\r
253 listeners.removeValue(listener, true);
\r
258 public void pause () {
\r
259 Gdx.app.log(TAG, "controllers paused");
\r
263 public void resume () {
\r
264 gatherControllers(true);
\r
265 Gdx.app.log(TAG, "controllers resumed");
\r
269 public void dispose () {
\r