changed build system for Android from Ant to Gradle
[rocksndiamonds.git] / build-projects / android / app / src / main / java / org / libsdl / app / SDLControllerManager.java
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
8 import android.content.Context;
9 import android.os.*;
10 import android.view.*;
11 import android.util.Log;
12
13
14 public class SDLControllerManager
15 {
16
17     public static native int nativeSetupJNI();
18
19     public static native int nativeAddJoystick(int device_id, String name, String desc,
20                                                int vendor_id, int product_id,
21                                                boolean is_accelerometer, int button_mask,
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         if (mJoystickHandler == null) {
40             if (Build.VERSION.SDK_INT >= 19) {
41                 mJoystickHandler = new SDLJoystickHandler_API19();
42             } else {
43                 mJoystickHandler = new SDLJoystickHandler_API16();
44             }
45         }
46
47         if (mHapticHandler == null) {
48             if (Build.VERSION.SDK_INT >= 26) {
49                 mHapticHandler = new SDLHapticHandler_API26();
50             } else {
51                 mHapticHandler = new SDLHapticHandler();
52             }
53         }
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, float intensity, int length) {
79         mHapticHandler.run(device_id, intensity, length);
80     }
81
82     /**
83      * This method is called by SDL using JNI.
84      */
85     public static void hapticStop(int device_id)
86     {
87         mHapticHandler.stop(device_id);
88     }
89
90     // Check if a given device is considered a possible SDL joystick
91     public static boolean isDeviceSDLJoystick(int deviceId) {
92         InputDevice device = InputDevice.getDevice(deviceId);
93         // We cannot use InputDevice.isVirtual before API 16, so let's accept
94         // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
95         if ((device == null) || (deviceId < 0)) {
96             return false;
97         }
98         int sources = device.getSources();
99
100         /* This is called for every button press, so let's not spam the logs */
101         /**
102         if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
103             Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
104         }
105         if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
106             Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
107         }
108         if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
109             Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
110         }
111         **/
112
113         return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
114                 ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
115                 ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
116         );
117     }
118
119 }
120
121 class SDLJoystickHandler {
122
123     /**
124      * Handles given MotionEvent.
125      * @param event the event to be handled.
126      * @return if given event was processed.
127      */
128     public boolean handleMotionEvent(MotionEvent event) {
129         return false;
130     }
131
132     /**
133      * Handles adding and removing of input devices.
134      */
135     public void pollInputDevices() {
136     }
137 }
138
139 /* Actual joystick functionality available for API >= 12 devices */
140 class SDLJoystickHandler_API16 extends SDLJoystickHandler {
141
142     static class SDLJoystick {
143         public int device_id;
144         public String name;
145         public String desc;
146         public ArrayList<InputDevice.MotionRange> axes;
147         public ArrayList<InputDevice.MotionRange> hats;
148     }
149     static class RangeComparator implements Comparator<InputDevice.MotionRange> {
150         @Override
151         public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
152             // Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
153             int arg0Axis = arg0.getAxis();
154             int arg1Axis = arg1.getAxis();
155             if (arg0Axis == MotionEvent.AXIS_GAS) {
156                 arg0Axis = MotionEvent.AXIS_BRAKE;
157             } else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
158                 arg0Axis = MotionEvent.AXIS_GAS;
159             }
160             if (arg1Axis == MotionEvent.AXIS_GAS) {
161                 arg1Axis = MotionEvent.AXIS_BRAKE;
162             } else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
163                 arg1Axis = MotionEvent.AXIS_GAS;
164             }
165
166             return arg0Axis - arg1Axis;
167         }
168     }
169
170     private ArrayList<SDLJoystick> mJoysticks;
171
172     public SDLJoystickHandler_API16() {
173
174         mJoysticks = new ArrayList<SDLJoystick>();
175     }
176
177     @Override
178     public void pollInputDevices() {
179         int[] deviceIds = InputDevice.getDeviceIds();
180         for(int i=0; i < deviceIds.length; ++i) {
181             SDLJoystick joystick = getJoystick(deviceIds[i]);
182             if (joystick == null) {
183                 joystick = new SDLJoystick();
184                 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
185                 if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
186                     joystick.device_id = deviceIds[i];
187                     joystick.name = joystickDevice.getName();
188                     joystick.desc = getJoystickDescriptor(joystickDevice);
189                     joystick.axes = new ArrayList<InputDevice.MotionRange>();
190                     joystick.hats = new ArrayList<InputDevice.MotionRange>();
191
192                     List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
193                     Collections.sort(ranges, new RangeComparator());
194                     for (InputDevice.MotionRange range : ranges ) {
195                         if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
196                             if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
197                                 range.getAxis() == MotionEvent.AXIS_HAT_Y) {
198                                 joystick.hats.add(range);
199                             }
200                             else {
201                                 joystick.axes.add(range);
202                             }
203                         }
204                     }
205
206                     mJoysticks.add(joystick);
207                     SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
208                 }
209             }
210         }
211
212         /* Check removed devices */
213         ArrayList<Integer> removedDevices = new ArrayList<Integer>();
214         for(int i=0; i < mJoysticks.size(); i++) {
215             int device_id = mJoysticks.get(i).device_id;
216             int j;
217             for (j=0; j < deviceIds.length; j++) {
218                 if (device_id == deviceIds[j]) break;
219             }
220             if (j == deviceIds.length) {
221                 removedDevices.add(Integer.valueOf(device_id));
222             }
223         }
224
225         for(int i=0; i < removedDevices.size(); i++) {
226             int device_id = removedDevices.get(i).intValue();
227             SDLControllerManager.nativeRemoveJoystick(device_id);
228             for (int j=0; j < mJoysticks.size(); j++) {
229                 if (mJoysticks.get(j).device_id == device_id) {
230                     mJoysticks.remove(j);
231                     break;
232                 }
233             }
234         }
235     }
236
237     protected SDLJoystick getJoystick(int device_id) {
238         for(int i=0; i < mJoysticks.size(); i++) {
239             if (mJoysticks.get(i).device_id == device_id) {
240                 return mJoysticks.get(i);
241             }
242         }
243         return null;
244     }
245
246     @Override
247     public boolean handleMotionEvent(MotionEvent event) {
248         if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
249             int actionPointerIndex = event.getActionIndex();
250             int action = event.getActionMasked();
251             switch(action) {
252                 case MotionEvent.ACTION_MOVE:
253                     SDLJoystick joystick = getJoystick(event.getDeviceId());
254                     if ( joystick != null ) {
255                         for (int i = 0; i < joystick.axes.size(); i++) {
256                             InputDevice.MotionRange range = joystick.axes.get(i);
257                             /* Normalize the value to -1...1 */
258                             float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
259                             SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
260                         }
261                         for (int i = 0; i < joystick.hats.size(); i+=2) {
262                             int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
263                             int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
264                             SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
265                         }
266                     }
267                     break;
268                 default:
269                     break;
270             }
271         }
272         return true;
273     }
274
275     public String getJoystickDescriptor(InputDevice joystickDevice) {
276         String desc = joystickDevice.getDescriptor();
277
278         if (desc != null && !desc.isEmpty()) {
279             return desc;
280         }
281
282         return joystickDevice.getName();
283     }
284     public int getProductId(InputDevice joystickDevice) {
285         return 0;
286     }
287     public int getVendorId(InputDevice joystickDevice) {
288         return 0;
289     }
290     public int getButtonMask(InputDevice joystickDevice) {
291         return -1;
292     }
293 }
294
295 class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
296
297     @Override
298     public int getProductId(InputDevice joystickDevice) {
299         return joystickDevice.getProductId();
300     }
301
302     @Override
303     public int getVendorId(InputDevice joystickDevice) {
304         return joystickDevice.getVendorId();
305     }
306
307     @Override
308     public int getButtonMask(InputDevice joystickDevice) {
309         int button_mask = 0;
310         int[] keys = new int[] {
311             KeyEvent.KEYCODE_BUTTON_A,
312             KeyEvent.KEYCODE_BUTTON_B,
313             KeyEvent.KEYCODE_BUTTON_X,
314             KeyEvent.KEYCODE_BUTTON_Y,
315             KeyEvent.KEYCODE_BACK,
316             KeyEvent.KEYCODE_BUTTON_MODE,
317             KeyEvent.KEYCODE_BUTTON_START,
318             KeyEvent.KEYCODE_BUTTON_THUMBL,
319             KeyEvent.KEYCODE_BUTTON_THUMBR,
320             KeyEvent.KEYCODE_BUTTON_L1,
321             KeyEvent.KEYCODE_BUTTON_R1,
322             KeyEvent.KEYCODE_DPAD_UP,
323             KeyEvent.KEYCODE_DPAD_DOWN,
324             KeyEvent.KEYCODE_DPAD_LEFT,
325             KeyEvent.KEYCODE_DPAD_RIGHT,
326             KeyEvent.KEYCODE_BUTTON_SELECT,
327             KeyEvent.KEYCODE_DPAD_CENTER,
328
329             // These don't map into any SDL controller buttons directly
330             KeyEvent.KEYCODE_BUTTON_L2,
331             KeyEvent.KEYCODE_BUTTON_R2,
332             KeyEvent.KEYCODE_BUTTON_C,
333             KeyEvent.KEYCODE_BUTTON_Z,
334             KeyEvent.KEYCODE_BUTTON_1,
335             KeyEvent.KEYCODE_BUTTON_2,
336             KeyEvent.KEYCODE_BUTTON_3,
337             KeyEvent.KEYCODE_BUTTON_4,
338             KeyEvent.KEYCODE_BUTTON_5,
339             KeyEvent.KEYCODE_BUTTON_6,
340             KeyEvent.KEYCODE_BUTTON_7,
341             KeyEvent.KEYCODE_BUTTON_8,
342             KeyEvent.KEYCODE_BUTTON_9,
343             KeyEvent.KEYCODE_BUTTON_10,
344             KeyEvent.KEYCODE_BUTTON_11,
345             KeyEvent.KEYCODE_BUTTON_12,
346             KeyEvent.KEYCODE_BUTTON_13,
347             KeyEvent.KEYCODE_BUTTON_14,
348             KeyEvent.KEYCODE_BUTTON_15,
349             KeyEvent.KEYCODE_BUTTON_16,
350         };
351         int[] masks = new int[] {
352             (1 << 0),   // A -> A
353             (1 << 1),   // B -> B
354             (1 << 2),   // X -> X
355             (1 << 3),   // Y -> Y
356             (1 << 4),   // BACK -> BACK
357             (1 << 5),   // MODE -> GUIDE
358             (1 << 6),   // START -> START
359             (1 << 7),   // THUMBL -> LEFTSTICK
360             (1 << 8),   // THUMBR -> RIGHTSTICK
361             (1 << 9),   // L1 -> LEFTSHOULDER
362             (1 << 10),  // R1 -> RIGHTSHOULDER
363             (1 << 11),  // DPAD_UP -> DPAD_UP
364             (1 << 12),  // DPAD_DOWN -> DPAD_DOWN
365             (1 << 13),  // DPAD_LEFT -> DPAD_LEFT
366             (1 << 14),  // DPAD_RIGHT -> DPAD_RIGHT
367             (1 << 4),   // SELECT -> BACK
368             (1 << 0),   // DPAD_CENTER -> A
369             (1 << 15),  // L2 -> ??
370             (1 << 16),  // R2 -> ??
371             (1 << 17),  // C -> ??
372             (1 << 18),  // Z -> ??
373             (1 << 20),  // 1 -> ??
374             (1 << 21),  // 2 -> ??
375             (1 << 22),  // 3 -> ??
376             (1 << 23),  // 4 -> ??
377             (1 << 24),  // 5 -> ??
378             (1 << 25),  // 6 -> ??
379             (1 << 26),  // 7 -> ??
380             (1 << 27),  // 8 -> ??
381             (1 << 28),  // 9 -> ??
382             (1 << 29),  // 10 -> ??
383             (1 << 30),  // 11 -> ??
384             (1 << 31),  // 12 -> ??
385             // We're out of room...
386             0xFFFFFFFF,  // 13 -> ??
387             0xFFFFFFFF,  // 14 -> ??
388             0xFFFFFFFF,  // 15 -> ??
389             0xFFFFFFFF,  // 16 -> ??
390         };
391         boolean[] has_keys = joystickDevice.hasKeys(keys);
392         for (int i = 0; i < keys.length; ++i) {
393             if (has_keys[i]) {
394                 button_mask |= masks[i];
395             }
396         }
397         return button_mask;
398     }
399 }
400
401 class SDLHapticHandler_API26 extends SDLHapticHandler {
402     @Override
403     public void run(int device_id, float intensity, int length) {
404         SDLHaptic haptic = getHaptic(device_id);
405         if (haptic != null) {
406             Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
407             if (intensity == 0.0f) {
408                 stop(device_id);
409                 return;
410             }
411
412             int vibeValue = Math.round(intensity * 255);
413
414             if (vibeValue > 255) {
415                 vibeValue = 255;
416             }
417             if (vibeValue < 1) {
418                 stop(device_id);
419                 return;
420             }
421             try {
422                 haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
423             }
424             catch (Exception e) {
425                 // Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
426                 // something went horribly wrong with the Android 8.0 APIs.
427                 haptic.vib.vibrate(length);
428             }
429         }
430     }
431 }
432
433 class SDLHapticHandler {
434
435     class SDLHaptic {
436         public int device_id;
437         public String name;
438         public Vibrator vib;
439     }
440
441     private ArrayList<SDLHaptic> mHaptics;
442
443     public SDLHapticHandler() {
444         mHaptics = new ArrayList<SDLHaptic>();
445     }
446
447     public void run(int device_id, float intensity, int length) {
448         SDLHaptic haptic = getHaptic(device_id);
449         if (haptic != null) {
450             haptic.vib.vibrate(length);
451         }
452     }
453
454     public void stop(int device_id) {
455         SDLHaptic haptic = getHaptic(device_id);
456         if (haptic != null) {
457             haptic.vib.cancel();
458         }
459     }
460
461     public void pollHapticDevices() {
462
463         final int deviceId_VIBRATOR_SERVICE = 999999;
464         boolean hasVibratorService = false;
465
466         int[] deviceIds = InputDevice.getDeviceIds();
467         // It helps processing the device ids in reverse order
468         // For example, in the case of the XBox 360 wireless dongle,
469         // so the first controller seen by SDL matches what the receiver
470         // considers to be the first controller
471
472         for (int i = deviceIds.length - 1; i > -1; i--) {
473             SDLHaptic haptic = getHaptic(deviceIds[i]);
474             if (haptic == null) {
475                 InputDevice device = InputDevice.getDevice(deviceIds[i]);
476                 Vibrator vib = device.getVibrator();
477                 if (vib.hasVibrator()) {
478                     haptic = new SDLHaptic();
479                     haptic.device_id = deviceIds[i];
480                     haptic.name = device.getName();
481                     haptic.vib = vib;
482                     mHaptics.add(haptic);
483                     SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
484                 }
485             }
486         }
487
488         /* Check VIBRATOR_SERVICE */
489         Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
490         if (vib != null) {
491             hasVibratorService = vib.hasVibrator();
492
493             if (hasVibratorService) {
494                 SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
495                 if (haptic == null) {
496                     haptic = new SDLHaptic();
497                     haptic.device_id = deviceId_VIBRATOR_SERVICE;
498                     haptic.name = "VIBRATOR_SERVICE";
499                     haptic.vib = vib;
500                     mHaptics.add(haptic);
501                     SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
502                 }
503             }
504         }
505
506         /* Check removed devices */
507         ArrayList<Integer> removedDevices = new ArrayList<Integer>();
508         for(int i=0; i < mHaptics.size(); i++) {
509             int device_id = mHaptics.get(i).device_id;
510             int j;
511             for (j=0; j < deviceIds.length; j++) {
512                 if (device_id == deviceIds[j]) break;
513             }
514
515             if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
516                 // don't remove the vibrator if it is still present
517             } else if (j == deviceIds.length) {
518                 removedDevices.add(device_id);
519             }
520         }
521
522         for(int i=0; i < removedDevices.size(); i++) {
523             int device_id = removedDevices.get(i);
524             SDLControllerManager.nativeRemoveHaptic(device_id);
525             for (int j=0; j < mHaptics.size(); j++) {
526                 if (mHaptics.get(j).device_id == device_id) {
527                     mHaptics.remove(j);
528                     break;
529                 }
530             }
531         }
532     }
533
534     protected SDLHaptic getHaptic(int device_id) {
535         for(int i=0; i < mHaptics.size(); i++) {
536             if (mHaptics.get(i).device_id == device_id) {
537                 return mHaptics.get(i);
538             }
539         }
540         return null;
541     }
542 }
543
544 class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
545     // Generic Motion (mouse hover, joystick...) events go here
546     @Override
547     public boolean onGenericMotion(View v, MotionEvent event) {
548         float x, y;
549         int action;
550
551         switch ( event.getSource() ) {
552             case InputDevice.SOURCE_JOYSTICK:
553             case InputDevice.SOURCE_GAMEPAD:
554             case InputDevice.SOURCE_DPAD:
555                 return SDLControllerManager.handleJoystickMotionEvent(event);
556
557             case InputDevice.SOURCE_MOUSE:
558                 action = event.getActionMasked();
559                 switch (action) {
560                     case MotionEvent.ACTION_SCROLL:
561                         x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
562                         y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
563                         SDLActivity.onNativeMouse(0, action, x, y, false);
564                         return true;
565
566                     case MotionEvent.ACTION_HOVER_MOVE:
567                         x = event.getX(0);
568                         y = event.getY(0);
569
570                         SDLActivity.onNativeMouse(0, action, x, y, false);
571                         return true;
572
573                     default:
574                         break;
575                 }
576                 break;
577
578             default:
579                 break;
580         }
581
582         // Event was not managed
583         return false;
584     }
585
586     public boolean supportsRelativeMouse() {
587         return false;
588     }
589
590     public boolean inRelativeMode() {
591         return false;
592     }
593
594     public boolean setRelativeMouseEnabled(boolean enabled) {
595         return false;
596     }
597
598     public void reclaimRelativeMouseModeIfNeeded()
599     {
600
601     }
602
603     public float getEventX(MotionEvent event) {
604         return event.getX(0);
605     }
606
607     public float getEventY(MotionEvent event) {
608         return event.getY(0);
609     }
610
611 }
612
613 class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
614     // Generic Motion (mouse hover, joystick...) events go here
615
616     private boolean mRelativeModeEnabled;
617
618     @Override
619     public boolean onGenericMotion(View v, MotionEvent event) {
620
621         // Handle relative mouse mode
622         if (mRelativeModeEnabled) {
623             if (event.getSource() == InputDevice.SOURCE_MOUSE) {
624                 int action = event.getActionMasked();
625                 if (action == MotionEvent.ACTION_HOVER_MOVE) {
626                     float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
627                     float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
628                     SDLActivity.onNativeMouse(0, action, x, y, true);
629                     return true;
630                 }
631             }
632         }
633
634         // Event was not managed, call SDLGenericMotionListener_API12 method
635         return super.onGenericMotion(v, event);
636     }
637
638     @Override
639     public boolean supportsRelativeMouse() {
640         return true;
641     }
642
643     @Override
644     public boolean inRelativeMode() {
645         return mRelativeModeEnabled;
646     }
647
648     @Override
649     public boolean setRelativeMouseEnabled(boolean enabled) {
650         mRelativeModeEnabled = enabled;
651         return true;
652     }
653
654     @Override
655     public float getEventX(MotionEvent event) {
656         if (mRelativeModeEnabled) {
657             return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
658         }
659         else {
660             return event.getX(0);
661         }
662     }
663
664     @Override
665     public float getEventY(MotionEvent event) {
666         if (mRelativeModeEnabled) {
667             return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
668         }
669         else {
670             return event.getY(0);
671         }
672     }
673 }
674
675
676 class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
677     // Generic Motion (mouse hover, joystick...) events go here
678     private boolean mRelativeModeEnabled;
679
680     @Override
681     public boolean onGenericMotion(View v, MotionEvent event) {
682         float x, y;
683         int action;
684
685         switch ( event.getSource() ) {
686             case InputDevice.SOURCE_JOYSTICK:
687             case InputDevice.SOURCE_GAMEPAD:
688             case InputDevice.SOURCE_DPAD:
689                 return SDLControllerManager.handleJoystickMotionEvent(event);
690
691             case InputDevice.SOURCE_MOUSE:
692             // DeX desktop mouse cursor is a separate non-standard input type.
693             case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
694                 action = event.getActionMasked();
695                 switch (action) {
696                     case MotionEvent.ACTION_SCROLL:
697                         x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
698                         y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
699                         SDLActivity.onNativeMouse(0, action, x, y, false);
700                         return true;
701
702                     case MotionEvent.ACTION_HOVER_MOVE:
703                         x = event.getX(0);
704                         y = event.getY(0);
705                         SDLActivity.onNativeMouse(0, action, x, y, false);
706                         return true;
707
708                     default:
709                         break;
710                 }
711                 break;
712
713             case InputDevice.SOURCE_MOUSE_RELATIVE:
714                 action = event.getActionMasked();
715                 switch (action) {
716                     case MotionEvent.ACTION_SCROLL:
717                         x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
718                         y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
719                         SDLActivity.onNativeMouse(0, action, x, y, false);
720                         return true;
721
722                     case MotionEvent.ACTION_HOVER_MOVE:
723                         x = event.getX(0);
724                         y = event.getY(0);
725                         SDLActivity.onNativeMouse(0, action, x, y, true);
726                         return true;
727
728                     default:
729                         break;
730                 }
731                 break;
732
733             default:
734                 break;
735         }
736
737         // Event was not managed
738         return false;
739     }
740
741     @Override
742     public boolean supportsRelativeMouse() {
743         return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
744     }
745
746     @Override
747     public boolean inRelativeMode() {
748         return mRelativeModeEnabled;
749     }
750
751     @Override
752     public boolean setRelativeMouseEnabled(boolean enabled) {
753         if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
754             if (enabled) {
755                 SDLActivity.getContentView().requestPointerCapture();
756             }
757             else {
758                 SDLActivity.getContentView().releasePointerCapture();
759             }
760             mRelativeModeEnabled = enabled;
761             return true;
762         }
763         else
764         {
765             return false;
766         }
767     }
768
769     @Override
770     public void reclaimRelativeMouseModeIfNeeded()
771     {
772         if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
773             SDLActivity.getContentView().requestPointerCapture();
774         }
775     }
776
777     @Override
778     public float getEventX(MotionEvent event) {
779         // Relative mouse in capture mode will only have relative for X/Y
780         return event.getX(0);
781     }
782
783     @Override
784     public float getEventY(MotionEvent event) {
785         // Relative mouse in capture mode will only have relative for X/Y
786         return event.getY(0);
787     }
788 }