|
@@ -41,13 +41,13 @@ import android.os.Build;
|
|
|
import android.util.Log;
|
|
|
import android.util.SparseArray;
|
|
|
import android.util.SparseIntArray;
|
|
|
+import android.view.GestureDetector;
|
|
|
import android.view.InputDevice;
|
|
|
-import android.view.InputDevice.MotionRange;
|
|
|
import android.view.KeyEvent;
|
|
|
import android.view.MotionEvent;
|
|
|
+import android.view.ScaleGestureDetector;
|
|
|
|
|
|
import java.util.Collections;
|
|
|
-import java.util.Comparator;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.Set;
|
|
|
|
|
@@ -55,21 +55,49 @@ import java.util.Set;
|
|
|
* Handles input related events for the {@link GodotView} view.
|
|
|
*/
|
|
|
public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
- private final String tag = this.getClass().getSimpleName();
|
|
|
+ private static final String TAG = GodotInputHandler.class.getSimpleName();
|
|
|
|
|
|
private final SparseIntArray mJoystickIds = new SparseIntArray(4);
|
|
|
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
|
|
|
|
|
|
private final GodotView godotView;
|
|
|
private final InputManager inputManager;
|
|
|
+ private final GestureDetector gestureDetector;
|
|
|
+ private final ScaleGestureDetector scaleGestureDetector;
|
|
|
+ private final GodotGestureHandler godotGestureHandler;
|
|
|
|
|
|
public GodotInputHandler(GodotView godotView) {
|
|
|
+ final Context context = godotView.getContext();
|
|
|
this.godotView = godotView;
|
|
|
- this.inputManager = (InputManager)godotView.getContext().getSystemService(Context.INPUT_SERVICE);
|
|
|
+ this.inputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
|
|
|
this.inputManager.registerInputDeviceListener(this, null);
|
|
|
+
|
|
|
+ this.godotGestureHandler = new GodotGestureHandler();
|
|
|
+ this.gestureDetector = new GestureDetector(context, godotGestureHandler);
|
|
|
+ this.gestureDetector.setIsLongpressEnabled(false);
|
|
|
+ this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
+ this.scaleGestureDetector.setStylusScaleEnabled(true);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private boolean isKeyEvent_GameDevice(int source) {
|
|
|
+ /**
|
|
|
+ * Enable long press events. This is false by default.
|
|
|
+ */
|
|
|
+ public void enableLongPress(boolean enable) {
|
|
|
+ this.gestureDetector.setIsLongpressEnabled(enable);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Enable multi-fingers pan & scale gestures. This is false by default.
|
|
|
+ *
|
|
|
+ * Note: This may interfere with multi-touch handling / support.
|
|
|
+ */
|
|
|
+ public void enablePanningAndScalingGestures(boolean enable) {
|
|
|
+ this.godotGestureHandler.setPanningAndScalingEnabled(enable);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isKeyEventGameDevice(int source) {
|
|
|
// Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD)
|
|
|
if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD))
|
|
|
return false;
|
|
@@ -87,7 +115,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
}
|
|
|
|
|
|
int source = event.getSource();
|
|
|
- if (isKeyEvent_GameDevice(source)) {
|
|
|
+ if (isKeyEventGameDevice(source)) {
|
|
|
// Check if the device exists
|
|
|
final int deviceId = event.getDeviceId();
|
|
|
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
|
|
@@ -122,11 +150,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
}
|
|
|
|
|
|
int source = event.getSource();
|
|
|
- //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
|
|
|
|
|
|
final int deviceId = event.getDeviceId();
|
|
|
// Check if source is a game device and that the device is a registered gamepad
|
|
|
- if (isKeyEvent_GameDevice(source)) {
|
|
|
+ if (isKeyEventGameDevice(source)) {
|
|
|
if (event.getRepeatCount() > 0) // ignore key echo
|
|
|
return true;
|
|
|
|
|
@@ -146,47 +173,36 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
}
|
|
|
|
|
|
public boolean onTouchEvent(final MotionEvent event) {
|
|
|
- // Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed
|
|
|
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
|
|
|
- if (event.getAction() != MotionEvent.ACTION_MOVE) {
|
|
|
- // we return true because every time a mouse event is fired, the event is already handled
|
|
|
- // in onGenericMotionEvent, so by touch event we can say that the event is also handled
|
|
|
- return true;
|
|
|
- }
|
|
|
- return handleMouseEvent(event);
|
|
|
+ this.scaleGestureDetector.onTouchEvent(event);
|
|
|
+ if (this.gestureDetector.onTouchEvent(event)) {
|
|
|
+ // The gesture detector has handled the event.
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- final int evcount = event.getPointerCount();
|
|
|
- if (evcount == 0)
|
|
|
+ if (godotGestureHandler.onMotionEvent(event)) {
|
|
|
+ // The gesture handler has handled the event.
|
|
|
return true;
|
|
|
+ }
|
|
|
|
|
|
- if (godotView != null) {
|
|
|
- final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc...
|
|
|
+ // Drag events are handled by the [GodotGestureHandler]
|
|
|
+ if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < event.getPointerCount(); i++) {
|
|
|
- arr[i * 3 + 0] = event.getPointerId(i);
|
|
|
- arr[i * 3 + 1] = event.getX(i);
|
|
|
- arr[i * 3 + 2] = event.getY(i);
|
|
|
- }
|
|
|
- final int action = event.getActionMasked();
|
|
|
- final int pointer_idx = event.getPointerId(event.getActionIndex());
|
|
|
-
|
|
|
- switch (action) {
|
|
|
- case MotionEvent.ACTION_DOWN:
|
|
|
- case MotionEvent.ACTION_CANCEL:
|
|
|
- case MotionEvent.ACTION_UP:
|
|
|
- case MotionEvent.ACTION_MOVE:
|
|
|
- case MotionEvent.ACTION_POINTER_UP:
|
|
|
- case MotionEvent.ACTION_POINTER_DOWN: {
|
|
|
- GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
|
|
|
- } break;
|
|
|
- }
|
|
|
+ if (isMouseEvent(event)) {
|
|
|
+ return handleMouseEvent(event);
|
|
|
}
|
|
|
- return true;
|
|
|
+
|
|
|
+ return handleTouchEvent(event);
|
|
|
}
|
|
|
|
|
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
|
|
- if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
|
|
|
+ // The gesture detector has handled the event.
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
|
|
|
// Check if the device exists
|
|
|
final int deviceId = event.getDeviceId();
|
|
|
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
|
|
@@ -199,15 +215,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
for (int i = 0; i < joystick.axes.size(); i++) {
|
|
|
final int axis = joystick.axes.get(i);
|
|
|
final float value = event.getAxisValue(axis);
|
|
|
- /**
|
|
|
- * As all axes are polled for each event, only fire an axis event if the value has actually changed.
|
|
|
- * Prevents flooding Godot with repeated events.
|
|
|
+ /*
|
|
|
+ As all axes are polled for each event, only fire an axis event if the value has actually changed.
|
|
|
+ Prevents flooding Godot with repeated events.
|
|
|
*/
|
|
|
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
|
|
|
// save value to prevent repeats
|
|
|
joystick.axesValues.put(axis, value);
|
|
|
- final int godotAxisIdx = i;
|
|
|
- GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
|
|
|
+ GodotLib.joyaxis(godotJoyId, i, value);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -222,16 +237,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
- } else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) {
|
|
|
- final float x = event.getX();
|
|
|
- final float y = event.getY();
|
|
|
- final int type = event.getAction();
|
|
|
- GodotLib.hover(type, x, y);
|
|
|
- return true;
|
|
|
- } else if ((event.isFromSource(InputDevice.SOURCE_MOUSE))) {
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
- return handleMouseEvent(event);
|
|
|
- }
|
|
|
+ } else if (isMouseEvent(event)) {
|
|
|
+ return handleMouseEvent(event);
|
|
|
}
|
|
|
|
|
|
return false;
|
|
@@ -243,7 +250,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
for (int deviceId : deviceIds) {
|
|
|
InputDevice device = inputManager.getInputDevice(deviceId);
|
|
|
if (DEBUG) {
|
|
|
- Log.v(tag, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
|
|
|
+ Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
|
|
|
}
|
|
|
onInputDeviceAdded(deviceId);
|
|
|
}
|
|
@@ -288,13 +295,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
joystick.name = device.getName();
|
|
|
|
|
|
//Helps with creating new joypad mappings.
|
|
|
- Log.i(tag, "=== New Input Device: " + joystick.name);
|
|
|
+ Log.i(TAG, "=== New Input Device: " + joystick.name);
|
|
|
|
|
|
Set<Integer> already = new HashSet<>();
|
|
|
for (InputDevice.MotionRange range : device.getMotionRanges()) {
|
|
|
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
|
|
|
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
|
|
|
- //Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad);
|
|
|
if (!isJoystick && !isGamepad) {
|
|
|
continue;
|
|
|
}
|
|
@@ -306,14 +312,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
already.add(axis);
|
|
|
joystick.axes.add(axis);
|
|
|
} else {
|
|
|
- Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
|
|
|
+ Log.w(TAG, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
Collections.sort(joystick.axes);
|
|
|
for (int idx = 0; idx < joystick.axes.size(); idx++) {
|
|
|
//Helps with creating new joypad mappings.
|
|
|
- Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
|
|
|
+ Log.i(TAG, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
|
|
|
}
|
|
|
mJoysticksDevices.put(deviceId, joystick);
|
|
|
|
|
@@ -338,13 +344,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
onInputDeviceAdded(deviceId);
|
|
|
}
|
|
|
|
|
|
- private static class RangeComparator implements Comparator<MotionRange> {
|
|
|
- @Override
|
|
|
- public int compare(MotionRange arg0, MotionRange arg1) {
|
|
|
- return arg0.getAxis() - arg1.getAxis();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
public static int getGodotButton(int keyCode) {
|
|
|
int button;
|
|
|
switch (keyCode) {
|
|
@@ -410,36 +409,107 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|
|
return button;
|
|
|
}
|
|
|
|
|
|
- private boolean handleMouseEvent(final MotionEvent event) {
|
|
|
- switch (event.getActionMasked()) {
|
|
|
+ static boolean isMouseEvent(MotionEvent event) {
|
|
|
+ return isMouseEvent(event.getSource());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isMouseEvent(int eventSource) {
|
|
|
+ return ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMotionEvent(final MotionEvent event) {
|
|
|
+ if (isMouseEvent(event)) {
|
|
|
+ return handleMouseEvent(event);
|
|
|
+ }
|
|
|
+
|
|
|
+ return handleTouchEvent(event);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) {
|
|
|
+ return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) {
|
|
|
+ if (isMouseEvent(eventSource)) {
|
|
|
+ return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ return handleTouchEvent(eventAction, x, y);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMouseEvent(final MotionEvent event) {
|
|
|
+ final int eventAction = event.getActionMasked();
|
|
|
+ final float x = event.getX();
|
|
|
+ final float y = event.getY();
|
|
|
+ final int buttonsMask = event.getButtonState();
|
|
|
+
|
|
|
+ final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
|
|
+ final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
|
|
|
+ return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
|
|
|
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) {
|
|
|
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick) {
|
|
|
+ switch (eventAction) {
|
|
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ // Zero-up the button state
|
|
|
+ buttonsMask = 0;
|
|
|
+ // FALL THROUGH
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
case MotionEvent.ACTION_HOVER_ENTER:
|
|
|
+ case MotionEvent.ACTION_HOVER_EXIT:
|
|
|
case MotionEvent.ACTION_HOVER_MOVE:
|
|
|
- case MotionEvent.ACTION_HOVER_EXIT: {
|
|
|
- final float x = event.getX();
|
|
|
- final float y = event.getY();
|
|
|
- final int type = event.getAction();
|
|
|
- GodotLib.hover(type, x, y);
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ case MotionEvent.ACTION_SCROLL: {
|
|
|
+ GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick);
|
|
|
return true;
|
|
|
}
|
|
|
- case MotionEvent.ACTION_BUTTON_PRESS:
|
|
|
- case MotionEvent.ACTION_BUTTON_RELEASE:
|
|
|
- case MotionEvent.ACTION_MOVE: {
|
|
|
- final float x = event.getX();
|
|
|
- final float y = event.getY();
|
|
|
- final int buttonsMask = event.getButtonState();
|
|
|
- final int action = event.getAction();
|
|
|
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleTouchEvent(final MotionEvent event) {
|
|
|
+ final int pointerCount = event.getPointerCount();
|
|
|
+ if (pointerCount == 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc...
|
|
|
+
|
|
|
+ for (int i = 0; i < pointerCount; i++) {
|
|
|
+ positions[i * 3 + 0] = event.getPointerId(i);
|
|
|
+ positions[i * 3 + 1] = event.getX(i);
|
|
|
+ positions[i * 3 + 2] = event.getY(i);
|
|
|
+ }
|
|
|
+ final int action = event.getActionMasked();
|
|
|
+ final int actionPointerId = event.getPointerId(event.getActionIndex());
|
|
|
+
|
|
|
+ return handleTouchEvent(action, actionPointerId, pointerCount, positions);
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleTouchEvent(int eventAction, float x, float y) {
|
|
|
+ return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y });
|
|
|
+ }
|
|
|
+
|
|
|
+ static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) {
|
|
|
+ switch (eventAction) {
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ case MotionEvent.ACTION_POINTER_UP:
|
|
|
+ case MotionEvent.ACTION_POINTER_DOWN: {
|
|
|
+ GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions);
|
|
|
return true;
|
|
|
}
|
|
|
- case MotionEvent.ACTION_SCROLL: {
|
|
|
- final float x = event.getX();
|
|
|
- final float y = event.getY();
|
|
|
- final int buttonsMask = event.getButtonState();
|
|
|
- final int action = event.getAction();
|
|
|
- final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
|
|
- final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
|
|
|
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);
|
|
|
- }
|
|
|
}
|
|
|
return false;
|
|
|
}
|