comparison android/src/org/libsdl/app/SDLControllerManager.java @ 1839:78abbabfd58d

Get Android build working again and update for SDL 2.0.7 (last version to support older versions of Android)
author Michael Pavone <pavone@retrodev.com>
date Sun, 14 Apr 2019 23:37:11 -0700
parents
children
comparison
equal deleted inserted replaced
1836:601ef72cc16f 1839:78abbabfd58d
1 package org.libsdl.app;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.List;
7 import java.util.Objects;
8
9 import android.content.Context;
10 import android.os.*;
11 import android.view.*;
12 import android.util.Log;
13
14
15 public class SDLControllerManager
16 {
17
18 public static native int nativeSetupJNI();
19
20 public static native int nativeAddJoystick(int device_id, String name, String desc,
21 int is_accelerometer, int nbuttons,
22 int naxes, int nhats, int nballs);
23 public static native int nativeRemoveJoystick(int device_id);
24 public static native int nativeAddHaptic(int device_id, String name);
25 public static native int nativeRemoveHaptic(int device_id);
26 public static native int onNativePadDown(int device_id, int keycode);
27 public static native int onNativePadUp(int device_id, int keycode);
28 public static native void onNativeJoy(int device_id, int axis,
29 float value);
30 public static native void onNativeHat(int device_id, int hat_id,
31 int x, int y);
32
33 protected static SDLJoystickHandler mJoystickHandler;
34 protected static SDLHapticHandler mHapticHandler;
35
36 private static final String TAG = "SDLControllerManager";
37
38 public static void initialize() {
39 mJoystickHandler = null;
40 mHapticHandler = null;
41
42 SDLControllerManager.setup();
43 }
44
45 public static void setup() {
46 if (Build.VERSION.SDK_INT >= 16) {
47 mJoystickHandler = new SDLJoystickHandler_API16();
48 } else if (Build.VERSION.SDK_INT >= 12) {
49 mJoystickHandler = new SDLJoystickHandler_API12();
50 } else {
51 mJoystickHandler = new SDLJoystickHandler();
52 }
53 mHapticHandler = new SDLHapticHandler();
54 }
55
56 // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
57 public static boolean handleJoystickMotionEvent(MotionEvent event) {
58 return mJoystickHandler.handleMotionEvent(event);
59 }
60
61 /**
62 * This method is called by SDL using JNI.
63 */
64 public static void pollInputDevices() {
65 mJoystickHandler.pollInputDevices();
66 }
67
68 /**
69 * This method is called by SDL using JNI.
70 */
71 public static void pollHapticDevices() {
72 mHapticHandler.pollHapticDevices();
73 }
74
75 /**
76 * This method is called by SDL using JNI.
77 */
78 public static void hapticRun(int device_id, int length) {
79 mHapticHandler.run(device_id, length);
80 }
81
82 // Check if a given device is considered a possible SDL joystick
83 public static boolean isDeviceSDLJoystick(int deviceId) {
84 InputDevice device = InputDevice.getDevice(deviceId);
85 // We cannot use InputDevice.isVirtual before API 16, so let's accept
86 // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
87 if ((device == null) || (deviceId < 0)) {
88 return false;
89 }
90 int sources = device.getSources();
91
92 if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
93 Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
94 }
95 if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
96 Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
97 }
98 if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
99 Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
100 }
101
102 return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
103 ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
104 ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
105 );
106 }
107
108 }
109
110 /* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
111 class SDLJoystickHandler {
112
113 /**
114 * Handles given MotionEvent.
115 * @param event the event to be handled.
116 * @return if given event was processed.
117 */
118 public boolean handleMotionEvent(MotionEvent event) {
119 return false;
120 }
121
122 /**
123 * Handles adding and removing of input devices.
124 */
125 public void pollInputDevices() {
126 }
127 }
128
129 /* Actual joystick functionality available for API >= 12 devices */
130 class SDLJoystickHandler_API12 extends SDLJoystickHandler {
131
132 static class SDLJoystick {
133 public int device_id;
134 public String name;
135 public String desc;
136 public ArrayList<InputDevice.MotionRange> axes;
137 public ArrayList<InputDevice.MotionRange> hats;
138 }
139 static class RangeComparator implements Comparator<InputDevice.MotionRange> {
140 @Override
141 public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
142 return arg0.getAxis() - arg1.getAxis();
143 }
144 }
145
146 private ArrayList<SDLJoystick> mJoysticks;
147
148 public SDLJoystickHandler_API12() {
149
150 mJoysticks = new ArrayList<SDLJoystick>();
151 }
152
153 @Override
154 public void pollInputDevices() {
155 int[] deviceIds = InputDevice.getDeviceIds();
156 // It helps processing the device ids in reverse order
157 // For example, in the case of the XBox 360 wireless dongle,
158 // so the first controller seen by SDL matches what the receiver
159 // considers to be the first controller
160
161 for(int i=deviceIds.length-1; i>-1; i--) {
162 SDLJoystick joystick = getJoystick(deviceIds[i]);
163 if (joystick == null) {
164 joystick = new SDLJoystick();
165 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
166 if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
167 joystick.device_id = deviceIds[i];
168 joystick.name = joystickDevice.getName();
169 joystick.desc = getJoystickDescriptor(joystickDevice);
170 joystick.axes = new ArrayList<InputDevice.MotionRange>();
171 joystick.hats = new ArrayList<InputDevice.MotionRange>();
172
173 List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
174 Collections.sort(ranges, new RangeComparator());
175 for (InputDevice.MotionRange range : ranges ) {
176 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
177 if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
178 range.getAxis() == MotionEvent.AXIS_HAT_Y) {
179 joystick.hats.add(range);
180 }
181 else {
182 joystick.axes.add(range);
183 }
184 }
185 }
186
187 mJoysticks.add(joystick);
188 SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
189 joystick.axes.size(), joystick.hats.size()/2, 0);
190 }
191 }
192 }
193
194 /* Check removed devices */
195 ArrayList<Integer> removedDevices = new ArrayList<Integer>();
196 for(int i=0; i < mJoysticks.size(); i++) {
197 int device_id = mJoysticks.get(i).device_id;
198 int j;
199 for (j=0; j < deviceIds.length; j++) {
200 if (device_id == deviceIds[j]) break;
201 }
202 if (j == deviceIds.length) {
203 removedDevices.add(Integer.valueOf(device_id));
204 }
205 }
206
207 for(int i=0; i < removedDevices.size(); i++) {
208 int device_id = removedDevices.get(i).intValue();
209 SDLControllerManager.nativeRemoveJoystick(device_id);
210 for (int j=0; j < mJoysticks.size(); j++) {
211 if (mJoysticks.get(j).device_id == device_id) {
212 mJoysticks.remove(j);
213 break;
214 }
215 }
216 }
217 }
218
219 protected SDLJoystick getJoystick(int device_id) {
220 for(int i=0; i < mJoysticks.size(); i++) {
221 if (mJoysticks.get(i).device_id == device_id) {
222 return mJoysticks.get(i);
223 }
224 }
225 return null;
226 }
227
228 @Override
229 public boolean handleMotionEvent(MotionEvent event) {
230 if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
231 int actionPointerIndex = event.getActionIndex();
232 int action = event.getActionMasked();
233 switch(action) {
234 case MotionEvent.ACTION_MOVE:
235 SDLJoystick joystick = getJoystick(event.getDeviceId());
236 if ( joystick != null ) {
237 for (int i = 0; i < joystick.axes.size(); i++) {
238 InputDevice.MotionRange range = joystick.axes.get(i);
239 /* Normalize the value to -1...1 */
240 float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
241 SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
242 }
243 for (int i = 0; i < joystick.hats.size(); i+=2) {
244 int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
245 int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
246 SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
247 }
248 }
249 break;
250 default:
251 break;
252 }
253 }
254 return true;
255 }
256
257 public String getJoystickDescriptor(InputDevice joystickDevice) {
258 return joystickDevice.getName();
259 }
260 }
261
262
263 class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
264
265 @Override
266 public String getJoystickDescriptor(InputDevice joystickDevice) {
267 String desc = joystickDevice.getDescriptor();
268
269 if (desc != null && !Objects.equals(desc, "")) {
270 return desc;
271 }
272
273 return super.getJoystickDescriptor(joystickDevice);
274 }
275 }
276
277 class SDLHapticHandler {
278
279 class SDLHaptic {
280 public int device_id;
281 public String name;
282 public Vibrator vib;
283 }
284
285 private ArrayList<SDLHaptic> mHaptics;
286
287 public SDLHapticHandler() {
288 mHaptics = new ArrayList<SDLHaptic>();
289 }
290
291 public void run(int device_id, int length) {
292 SDLHaptic haptic = getHaptic(device_id);
293 if (haptic != null) {
294 haptic.vib.vibrate (length);
295 }
296 }
297
298 public void pollHapticDevices() {
299
300 final int deviceId_VIBRATOR_SERVICE = 999999;
301 boolean hasVibratorService = false;
302
303 int[] deviceIds = InputDevice.getDeviceIds();
304 // It helps processing the device ids in reverse order
305 // For example, in the case of the XBox 360 wireless dongle,
306 // so the first controller seen by SDL matches what the receiver
307 // considers to be the first controller
308
309 if (Build.VERSION.SDK_INT >= 16)
310 {
311 for (int i = deviceIds.length - 1; i > -1; i--) {
312 SDLHaptic haptic = getHaptic(deviceIds[i]);
313 if (haptic == null) {
314 InputDevice device = InputDevice.getDevice(deviceIds[i]);
315 Vibrator vib = device.getVibrator();
316 if (vib.hasVibrator()) {
317 haptic = new SDLHaptic();
318 haptic.device_id = deviceIds[i];
319 haptic.name = device.getName();
320 haptic.vib = vib;
321 mHaptics.add(haptic);
322 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
323 }
324 }
325 }
326 }
327
328 /* Check VIBRATOR_SERVICE */
329 Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
330 if (vib != null) {
331 if (Build.VERSION.SDK_INT >= 11) {
332 hasVibratorService = vib.hasVibrator();
333 } else {
334 hasVibratorService = true;
335 }
336
337 if (hasVibratorService) {
338 SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
339 if (haptic == null) {
340 haptic = new SDLHaptic();
341 haptic.device_id = deviceId_VIBRATOR_SERVICE;
342 haptic.name = "VIBRATOR_SERVICE";
343 haptic.vib = vib;
344 mHaptics.add(haptic);
345 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
346 }
347 }
348 }
349
350 /* Check removed devices */
351 ArrayList<Integer> removedDevices = new ArrayList<Integer>();
352 for(int i=0; i < mHaptics.size(); i++) {
353 int device_id = mHaptics.get(i).device_id;
354 int j;
355 for (j=0; j < deviceIds.length; j++) {
356 if (device_id == deviceIds[j]) break;
357 }
358
359 if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
360 // don't remove the vibrator if it is still present
361 } else if (j == deviceIds.length) {
362 removedDevices.add(device_id);
363 }
364 }
365
366 for(int i=0; i < removedDevices.size(); i++) {
367 int device_id = removedDevices.get(i);
368 SDLControllerManager.nativeRemoveHaptic(device_id);
369 for (int j=0; j < mHaptics.size(); j++) {
370 if (mHaptics.get(j).device_id == device_id) {
371 mHaptics.remove(j);
372 break;
373 }
374 }
375 }
376 }
377
378 protected SDLHaptic getHaptic(int device_id) {
379 for(int i=0; i < mHaptics.size(); i++) {
380 if (mHaptics.get(i).device_id == device_id) {
381 return mHaptics.get(i);
382 }
383 }
384 return null;
385 }
386 }
387
388 class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
389 // Generic Motion (mouse hover, joystick...) events go here
390 @Override
391 public boolean onGenericMotion(View v, MotionEvent event) {
392 float x, y;
393 int action;
394
395 switch ( event.getSource() ) {
396 case InputDevice.SOURCE_JOYSTICK:
397 case InputDevice.SOURCE_GAMEPAD:
398 case InputDevice.SOURCE_DPAD:
399 return SDLControllerManager.handleJoystickMotionEvent(event);
400
401 case InputDevice.SOURCE_MOUSE:
402 if (!SDLActivity.mSeparateMouseAndTouch) {
403 break;
404 }
405 action = event.getActionMasked();
406 switch (action) {
407 case MotionEvent.ACTION_SCROLL:
408 x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
409 y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
410 SDLActivity.onNativeMouse(0, action, x, y);
411 return true;
412
413 case MotionEvent.ACTION_HOVER_MOVE:
414 x = event.getX(0);
415 y = event.getY(0);
416
417 SDLActivity.onNativeMouse(0, action, x, y);
418 return true;
419
420 default:
421 break;
422 }
423 break;
424
425 default:
426 break;
427 }
428
429 // Event was not managed
430 return false;
431 }
432 }
433