DEADSOFTWARE

Fix crash on android 4.1
[d2df-sdl.git] / android / src / org / libsdl / app / SDLControllerManager.java
1 package org.libsdl.app;
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.List;
8 import android.content.Context;
9 import android.os.*;
10 import android.view.*;
11 import android.util.Log;
14 public class SDLControllerManager
15 {
17 public static native int nativeSetupJNI();
19 public static native int nativeAddJoystick(int device_id, String name, String desc,
20 int is_accelerometer, int nbuttons,
21 int naxes, int nhats, int nballs);
22 public static native int nativeRemoveJoystick(int device_id);
23 public static native int nativeAddHaptic(int device_id, String name);
24 public static native int nativeRemoveHaptic(int device_id);
25 public static native int onNativePadDown(int device_id, int keycode);
26 public static native int onNativePadUp(int device_id, int keycode);
27 public static native void onNativeJoy(int device_id, int axis,
28 float value);
29 public static native void onNativeHat(int device_id, int hat_id,
30 int x, int y);
32 protected static SDLJoystickHandler mJoystickHandler;
33 protected static SDLHapticHandler mHapticHandler;
35 private static final String TAG = "SDLControllerManager";
37 public static void initialize() {
38 mJoystickHandler = null;
39 mHapticHandler = null;
41 SDLControllerManager.setup();
42 }
44 public static void setup() {
45 if (Build.VERSION.SDK_INT >= 16) {
46 mJoystickHandler = new SDLJoystickHandler_API16();
47 } else if (Build.VERSION.SDK_INT >= 12) {
48 mJoystickHandler = new SDLJoystickHandler_API12();
49 } else {
50 mJoystickHandler = new SDLJoystickHandler();
51 }
52 mHapticHandler = new SDLHapticHandler();
53 }
55 // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
56 public static boolean handleJoystickMotionEvent(MotionEvent event) {
57 return mJoystickHandler.handleMotionEvent(event);
58 }
60 /**
61 * This method is called by SDL using JNI.
62 */
63 public static void pollInputDevices() {
64 mJoystickHandler.pollInputDevices();
65 }
67 /**
68 * This method is called by SDL using JNI.
69 */
70 public static void pollHapticDevices() {
71 mHapticHandler.pollHapticDevices();
72 }
74 /**
75 * This method is called by SDL using JNI.
76 */
77 public static void hapticRun(int device_id, int length) {
78 mHapticHandler.run(device_id, length);
79 }
81 // Check if a given device is considered a possible SDL joystick
82 public static boolean isDeviceSDLJoystick(int deviceId) {
83 InputDevice device = InputDevice.getDevice(deviceId);
84 // We cannot use InputDevice.isVirtual before API 16, so let's accept
85 // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
86 if ((device == null) || (deviceId < 0)) {
87 return false;
88 }
89 int sources = device.getSources();
91 if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
92 Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
93 }
94 if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
95 Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
96 }
97 if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
98 Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
99 }
101 return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
102 ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
103 ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
104 );
109 /* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
110 class SDLJoystickHandler {
112 /**
113 * Handles given MotionEvent.
114 * @param event the event to be handled.
115 * @return if given event was processed.
116 */
117 public boolean handleMotionEvent(MotionEvent event) {
118 return false;
121 /**
122 * Handles adding and removing of input devices.
123 */
124 public void pollInputDevices() {
128 /* Actual joystick functionality available for API >= 12 devices */
129 class SDLJoystickHandler_API12 extends SDLJoystickHandler {
131 static class SDLJoystick {
132 public int device_id;
133 public String name;
134 public String desc;
135 public ArrayList<InputDevice.MotionRange> axes;
136 public ArrayList<InputDevice.MotionRange> hats;
138 static class RangeComparator implements Comparator<InputDevice.MotionRange> {
139 @Override
140 public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
141 return arg0.getAxis() - arg1.getAxis();
145 private ArrayList<SDLJoystick> mJoysticks;
147 public SDLJoystickHandler_API12() {
149 mJoysticks = new ArrayList<SDLJoystick>();
152 @Override
153 public void pollInputDevices() {
154 int[] deviceIds = InputDevice.getDeviceIds();
155 // It helps processing the device ids in reverse order
156 // For example, in the case of the XBox 360 wireless dongle,
157 // so the first controller seen by SDL matches what the receiver
158 // considers to be the first controller
160 for(int i=deviceIds.length-1; i>-1; i--) {
161 SDLJoystick joystick = getJoystick(deviceIds[i]);
162 if (joystick == null) {
163 joystick = new SDLJoystick();
164 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
165 if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
166 joystick.device_id = deviceIds[i];
167 joystick.name = joystickDevice.getName();
168 joystick.desc = getJoystickDescriptor(joystickDevice);
169 joystick.axes = new ArrayList<InputDevice.MotionRange>();
170 joystick.hats = new ArrayList<InputDevice.MotionRange>();
172 List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
173 Collections.sort(ranges, new RangeComparator());
174 for (InputDevice.MotionRange range : ranges ) {
175 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
176 if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
177 range.getAxis() == MotionEvent.AXIS_HAT_Y) {
178 joystick.hats.add(range);
180 else {
181 joystick.axes.add(range);
186 mJoysticks.add(joystick);
187 SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
188 joystick.axes.size(), joystick.hats.size()/2, 0);
193 /* Check removed devices */
194 ArrayList<Integer> removedDevices = new ArrayList<Integer>();
195 for(int i=0; i < mJoysticks.size(); i++) {
196 int device_id = mJoysticks.get(i).device_id;
197 int j;
198 for (j=0; j < deviceIds.length; j++) {
199 if (device_id == deviceIds[j]) break;
201 if (j == deviceIds.length) {
202 removedDevices.add(Integer.valueOf(device_id));
206 for(int i=0; i < removedDevices.size(); i++) {
207 int device_id = removedDevices.get(i).intValue();
208 SDLControllerManager.nativeRemoveJoystick(device_id);
209 for (int j=0; j < mJoysticks.size(); j++) {
210 if (mJoysticks.get(j).device_id == device_id) {
211 mJoysticks.remove(j);
212 break;
218 protected SDLJoystick getJoystick(int device_id) {
219 for(int i=0; i < mJoysticks.size(); i++) {
220 if (mJoysticks.get(i).device_id == device_id) {
221 return mJoysticks.get(i);
224 return null;
227 @Override
228 public boolean handleMotionEvent(MotionEvent event) {
229 if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
230 int actionPointerIndex = event.getActionIndex();
231 int action = event.getActionMasked();
232 switch(action) {
233 case MotionEvent.ACTION_MOVE:
234 SDLJoystick joystick = getJoystick(event.getDeviceId());
235 if ( joystick != null ) {
236 for (int i = 0; i < joystick.axes.size(); i++) {
237 InputDevice.MotionRange range = joystick.axes.get(i);
238 /* Normalize the value to -1...1 */
239 float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
240 SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
242 for (int i = 0; i < joystick.hats.size(); i+=2) {
243 int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
244 int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
245 SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
248 break;
249 default:
250 break;
253 return true;
256 public String getJoystickDescriptor(InputDevice joystickDevice) {
257 return joystickDevice.getName();
262 class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
264 @Override
265 public String getJoystickDescriptor(InputDevice joystickDevice) {
266 String desc = joystickDevice.getDescriptor();
268 if (desc != null && !desc.equals("")) {
269 return desc;
272 return super.getJoystickDescriptor(joystickDevice);
276 class SDLHapticHandler {
278 class SDLHaptic {
279 public int device_id;
280 public String name;
281 public Vibrator vib;
284 private ArrayList<SDLHaptic> mHaptics;
286 public SDLHapticHandler() {
287 mHaptics = new ArrayList<SDLHaptic>();
290 public void run(int device_id, int length) {
291 SDLHaptic haptic = getHaptic(device_id);
292 if (haptic != null) {
293 haptic.vib.vibrate (length);
297 public void pollHapticDevices() {
299 final int deviceId_VIBRATOR_SERVICE = 999999;
300 boolean hasVibratorService = false;
302 int[] deviceIds = InputDevice.getDeviceIds();
303 // It helps processing the device ids in reverse order
304 // For example, in the case of the XBox 360 wireless dongle,
305 // so the first controller seen by SDL matches what the receiver
306 // considers to be the first controller
308 if (Build.VERSION.SDK_INT >= 16)
310 for (int i = deviceIds.length - 1; i > -1; i--) {
311 SDLHaptic haptic = getHaptic(deviceIds[i]);
312 if (haptic == null) {
313 InputDevice device = InputDevice.getDevice(deviceIds[i]);
314 Vibrator vib = device.getVibrator();
315 if (vib.hasVibrator()) {
316 haptic = new SDLHaptic();
317 haptic.device_id = deviceIds[i];
318 haptic.name = device.getName();
319 haptic.vib = vib;
320 mHaptics.add(haptic);
321 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
327 /* Check VIBRATOR_SERVICE */
328 Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
329 if (vib != null) {
330 if (Build.VERSION.SDK_INT >= 11) {
331 hasVibratorService = vib.hasVibrator();
332 } else {
333 hasVibratorService = true;
336 if (hasVibratorService) {
337 SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
338 if (haptic == null) {
339 haptic = new SDLHaptic();
340 haptic.device_id = deviceId_VIBRATOR_SERVICE;
341 haptic.name = "VIBRATOR_SERVICE";
342 haptic.vib = vib;
343 mHaptics.add(haptic);
344 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
349 /* Check removed devices */
350 ArrayList<Integer> removedDevices = new ArrayList<Integer>();
351 for(int i=0; i < mHaptics.size(); i++) {
352 int device_id = mHaptics.get(i).device_id;
353 int j;
354 for (j=0; j < deviceIds.length; j++) {
355 if (device_id == deviceIds[j]) break;
358 if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
359 // don't remove the vibrator if it is still present
360 } else if (j == deviceIds.length) {
361 removedDevices.add(device_id);
365 for(int i=0; i < removedDevices.size(); i++) {
366 int device_id = removedDevices.get(i);
367 SDLControllerManager.nativeRemoveHaptic(device_id);
368 for (int j=0; j < mHaptics.size(); j++) {
369 if (mHaptics.get(j).device_id == device_id) {
370 mHaptics.remove(j);
371 break;
377 protected SDLHaptic getHaptic(int device_id) {
378 for(int i=0; i < mHaptics.size(); i++) {
379 if (mHaptics.get(i).device_id == device_id) {
380 return mHaptics.get(i);
383 return null;
384 }
387 class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
388 // Generic Motion (mouse hover, joystick...) events go here
389 @Override
390 public boolean onGenericMotion(View v, MotionEvent event) {
391 float x, y;
392 int action;
394 switch ( event.getSource() ) {
395 case InputDevice.SOURCE_JOYSTICK:
396 case InputDevice.SOURCE_GAMEPAD:
397 case InputDevice.SOURCE_DPAD:
398 return SDLControllerManager.handleJoystickMotionEvent(event);
400 case InputDevice.SOURCE_MOUSE:
401 if (!SDLActivity.mSeparateMouseAndTouch) {
402 break;
404 action = event.getActionMasked();
405 switch (action) {
406 case MotionEvent.ACTION_SCROLL:
407 x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
408 y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
409 SDLActivity.onNativeMouse(0, action, x, y);
410 return true;
412 case MotionEvent.ACTION_HOVER_MOVE:
413 x = event.getX(0);
414 y = event.getY(0);
416 SDLActivity.onNativeMouse(0, action, x, y);
417 return true;
419 default:
420 break;
422 break;
424 default:
425 break;
428 // Event was not managed
429 return false;