Forráskód Böngészése

Cleanup the Android input logic implementation

Fredia Huya-Kouadio 3 éve
szülő
commit
440fa10246

+ 112 - 88
platform/android/android_input_handler.cpp

@@ -57,10 +57,6 @@ void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> e
 	ev->set_control(control_mem);
 }
 
-void AndroidInputHandler::process_event(Ref<InputEvent> &p_event) {
-	input->parse_input_event(p_event);
-}
-
 void AndroidInputHandler::process_key_event(int p_scancode, int p_physical_scancode, int p_unicode, bool p_pressed) {
 	Ref<InputEventKey> ev;
 	ev.instance();
@@ -104,20 +100,31 @@ void AndroidInputHandler::process_key_event(int p_scancode, int p_physical_scanc
 	input->parse_input_event(ev);
 }
 
-void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points) {
+void AndroidInputHandler::_parse_all_touch(bool p_pressed) {
+	if (touch.size()) {
+		//end all if exist
+		for (int i = 0; i < touch.size(); i++) {
+			Ref<InputEventScreenTouch> ev;
+			ev.instance();
+			ev->set_index(touch[i].id);
+			ev->set_pressed(p_pressed);
+			ev->set_position(touch[i].pos);
+			input->parse_input_event(ev);
+		}
+	}
+}
+
+void AndroidInputHandler::_release_all_touch() {
+	_parse_all_touch(false);
+	touch.clear();
+}
+
+void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) {
 	switch (p_event) {
 		case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
-			if (touch.size()) {
-				//end all if exist
-				for (int i = 0; i < touch.size(); i++) {
-					Ref<InputEventScreenTouch> ev;
-					ev.instance();
-					ev->set_index(touch[i].id);
-					ev->set_pressed(false);
-					ev->set_position(touch[i].pos);
-					input->parse_input_event(ev);
-				}
-			}
+			// Release any remaining touches or mouse event
+			_release_mouse_event_info();
+			_release_all_touch();
 
 			touch.resize(p_points.size());
 			for (int i = 0; i < p_points.size(); i++) {
@@ -126,18 +133,13 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
 			}
 
 			//send touch
-			for (int i = 0; i < touch.size(); i++) {
-				Ref<InputEventScreenTouch> ev;
-				ev.instance();
-				ev->set_index(touch[i].id);
-				ev->set_pressed(true);
-				ev->set_position(touch[i].pos);
-				input->parse_input_event(ev);
-			}
+			_parse_all_touch(true);
 
 		} break;
 		case AMOTION_EVENT_ACTION_MOVE: { //motion
-			ERR_FAIL_COND(touch.size() != p_points.size());
+			if (touch.size() != p_points.size()) {
+				return;
+			}
 
 			for (int i = 0; i < touch.size(); i++) {
 				int idx = -1;
@@ -166,18 +168,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
 		} break;
 		case AMOTION_EVENT_ACTION_CANCEL:
 		case AMOTION_EVENT_ACTION_UP: { //release
-			if (touch.size()) {
-				//end all if exist
-				for (int i = 0; i < touch.size(); i++) {
-					Ref<InputEventScreenTouch> ev;
-					ev.instance();
-					ev->set_index(touch[i].id);
-					ev->set_pressed(false);
-					ev->set_position(touch[i].pos);
-					input->parse_input_event(ev);
-				}
-				touch.clear();
-			}
+			_release_all_touch();
 		} break;
 		case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
 			for (int i = 0; i < p_points.size(); i++) {
@@ -215,73 +206,102 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
 	}
 }
 
-void AndroidInputHandler::process_hover(int p_type, Point2 p_pos) {
-	// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
-	switch (p_type) {
+void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click) {
+	if (!mouse_event_info.valid) {
+		return;
+	}
+
+	Ref<InputEventMouseButton> ev;
+	ev.instance();
+	_set_key_modifier_state(ev);
+	ev->set_position(mouse_event_info.pos);
+	ev->set_global_position(mouse_event_info.pos);
+	ev->set_pressed(p_pressed);
+	int changed_button_mask = buttons_state ^ buttons_mask;
+
+	buttons_state = buttons_mask;
+
+	ev->set_button_index(_button_index_from_mask(changed_button_mask));
+	ev->set_button_mask(buttons_mask);
+	ev->set_doubleclick(p_double_click);
+	input->parse_input_event(ev);
+	hover_prev_pos = mouse_event_info.pos;
+}
+
+void AndroidInputHandler::_release_mouse_event_info() {
+	_parse_mouse_event_info(0, false, false);
+	mouse_event_info.valid = false;
+}
+
+void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click) {
+	int event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
+	switch (p_event_action) {
 		case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
 		case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
 		case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
+			// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
 			Ref<InputEventMouseMotion> ev;
 			ev.instance();
 			_set_key_modifier_state(ev);
-			ev->set_position(p_pos);
-			ev->set_global_position(p_pos);
-			ev->set_relative(p_pos - hover_prev_pos);
+			ev->set_position(p_event_pos);
+			ev->set_global_position(p_event_pos);
+			ev->set_relative(p_event_pos - hover_prev_pos);
 			input->parse_input_event(ev);
-			hover_prev_pos = p_pos;
+			hover_prev_pos = p_event_pos;
 		} break;
-	}
-}
 
-void AndroidInputHandler::process_mouse_event(int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
-	int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
-	switch (event_action) {
-		case AMOTION_EVENT_ACTION_BUTTON_PRESS:
-		case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
-			Ref<InputEventMouseButton> ev;
-			ev.instance();
-			_set_key_modifier_state(ev);
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
-			ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
-			int changed_button_mask = buttons_state ^ event_buttons_mask;
+		case AMOTION_EVENT_ACTION_DOWN:
+		case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
+			// Release any remaining touches or mouse event
+			_release_mouse_event_info();
+			_release_all_touch();
 
-			buttons_state = event_buttons_mask;
+			mouse_event_info.valid = true;
+			mouse_event_info.pos = p_event_pos;
+			_parse_mouse_event_info(event_buttons_mask, true, p_double_click);
+		} break;
 
-			ev->set_button_index(_button_index_from_mask(changed_button_mask));
-			ev->set_button_mask(event_buttons_mask);
-			input->parse_input_event(ev);
+		case AMOTION_EVENT_ACTION_UP:
+		case AMOTION_EVENT_ACTION_CANCEL:
+		case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+			_release_mouse_event_info();
 		} break;
 
 		case AMOTION_EVENT_ACTION_MOVE: {
+			if (!mouse_event_info.valid) {
+				return;
+			}
+
 			Ref<InputEventMouseMotion> ev;
 			ev.instance();
 			_set_key_modifier_state(ev);
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
-			ev->set_relative(event_pos - hover_prev_pos);
+			ev->set_position(p_event_pos);
+			ev->set_global_position(p_event_pos);
+			ev->set_relative(p_event_pos - hover_prev_pos);
 			ev->set_button_mask(event_buttons_mask);
 			input->parse_input_event(ev);
-			hover_prev_pos = event_pos;
+			mouse_event_info.pos = p_event_pos;
+			hover_prev_pos = p_event_pos;
 		} break;
+
 		case AMOTION_EVENT_ACTION_SCROLL: {
 			Ref<InputEventMouseButton> ev;
 			ev.instance();
 			_set_key_modifier_state(ev);
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
+			ev->set_position(p_event_pos);
+			ev->set_global_position(p_event_pos);
 			ev->set_pressed(true);
 			buttons_state = event_buttons_mask;
-			if (event_vertical_factor > 0) {
-				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, event_vertical_factor);
-			} else if (event_vertical_factor < 0) {
-				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -event_vertical_factor);
+			if (p_delta.y > 0) {
+				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, p_delta.y);
+			} else if (p_delta.y < 0) {
+				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -p_delta.y);
 			}
 
-			if (event_horizontal_factor > 0) {
-				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, event_horizontal_factor);
-			} else if (event_horizontal_factor < 0) {
-				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -event_horizontal_factor);
+			if (p_delta.x > 0) {
+				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, p_delta.x);
+			} else if (p_delta.x < 0) {
+				_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -p_delta.x);
 			}
 		} break;
 	}
@@ -299,18 +319,22 @@ void AndroidInputHandler::_wheel_button_click(int event_buttons_mask, const Ref<
 	input->parse_input_event(evdd);
 }
 
-void AndroidInputHandler::process_double_tap(int event_android_button_mask, Point2 p_pos) {
-	int event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
-	Ref<InputEventMouseButton> ev;
-	ev.instance();
-	_set_key_modifier_state(ev);
-	ev->set_position(p_pos);
-	ev->set_global_position(p_pos);
-	ev->set_pressed(event_button_mask != 0);
-	ev->set_button_index(_button_index_from_mask(event_button_mask));
-	ev->set_button_mask(event_button_mask);
-	ev->set_doubleclick(true);
-	input->parse_input_event(ev);
+void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
+	Ref<InputEventMagnifyGesture> magnify_event;
+	magnify_event.instance();
+	_set_key_modifier_state(magnify_event);
+	magnify_event->set_position(p_pos);
+	magnify_event->set_factor(p_factor);
+	input->parse_input_event(magnify_event);
+}
+
+void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
+	Ref<InputEventPanGesture> pan_event;
+	pan_event.instance();
+	_set_key_modifier_state(pan_event);
+	pan_event->set_position(p_pos);
+	pan_event->set_delta(p_delta);
+	input->parse_input_event(pan_event);
 }
 
 int AndroidInputHandler::_button_index_from_mask(int button_mask) {

+ 18 - 5
platform/android/android_input_handler.h

@@ -44,6 +44,11 @@ public:
 		Point2 pos;
 	};
 
+	struct MouseEventInfo {
+		bool valid = false;
+		Point2 pos;
+	};
+
 	enum {
 		JOY_EVENT_BUTTON = 0,
 		JOY_EVENT_AXIS = 1,
@@ -61,6 +66,7 @@ public:
 
 private:
 	Vector<TouchPos> touch;
+	MouseEventInfo mouse_event_info;
 	Point2 hover_prev_pos; // needed to calculate the relative position on hover events
 
 	bool alt_mem = false;
@@ -80,14 +86,21 @@ private:
 
 	void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor);
 
+	void _parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click);
+
+	void _release_mouse_event_info();
+
+	void _parse_all_touch(bool p_pressed);
+
+	void _release_all_touch();
+
 public:
-	void process_event(Ref<InputEvent> &p_event);
 	void process_joy_event(const JoypadEvent &p_event);
 	void process_key_event(int p_scancode, int p_physical_scancode, int p_unicode, bool p_pressed);
-	void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
-	void process_hover(int p_type, Point2 p_pos);
-	void process_mouse_event(int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor);
-	void process_double_tap(int event_android_button_mask, Point2 p_pos);
+	void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click);
+	void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
+	void process_magnify(Point2 p_pos, float p_factor);
+	void process_pan(Point2 p_pos, Vector2 p_delta);
 	void joy_connection_changed(int p_device, bool p_connected, String p_name);
 };
 

+ 16 - 19
platform/android/java/lib/src/org/godotengine/godot/GodotLib.java

@@ -82,7 +82,7 @@ public class GodotLib {
 	public static native void newcontext();
 
 	/**
-	 * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
+	 * Forward {@link Activity#onBackPressed()} event.
 	 */
 	public static native void back();
 
@@ -98,63 +98,60 @@ public class GodotLib {
 	public static native void ttsCallback(int event, int id, int pos);
 
 	/**
-	 * Forward touch events from the main thread to the GL thread.
+	 * Forward touch events.
 	 */
-	public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions);
-	public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask);
-	public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor);
+	public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions);
 
 	/**
-	 * Forward hover events from the main thread to the GL thread.
+	 * Dispatch mouse events
 	 */
-	public static native void hover(int type, float x, float y);
+	public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick);
 
-	/**
-	 * Forward double_tap events from the main thread to the GL thread.
-	 */
-	public static native void doubleTap(int buttonMask, int x, int y);
+	public static native void magnify(float x, float y, float factor);
+
+	public static native void pan(float x, float y, float deltaX, float deltaY);
 
 	/**
-	 * Forward accelerometer sensor events from the main thread to the GL thread.
+	 * Forward accelerometer sensor events.
 	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
 	 */
 	public static native void accelerometer(float x, float y, float z);
 
 	/**
-	 * Forward gravity sensor events from the main thread to the GL thread.
+	 * Forward gravity sensor events.
 	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
 	 */
 	public static native void gravity(float x, float y, float z);
 
 	/**
-	 * Forward magnetometer sensor events from the main thread to the GL thread.
+	 * Forward magnetometer sensor events.
 	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
 	 */
 	public static native void magnetometer(float x, float y, float z);
 
 	/**
-	 * Forward gyroscope sensor events from the main thread to the GL thread.
+	 * Forward gyroscope sensor events.
 	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
 	 */
 	public static native void gyroscope(float x, float y, float z);
 
 	/**
-	 * Forward regular key events from the main thread to the GL thread.
+	 * Forward regular key events.
 	 */
 	public static native void key(int p_scancode, int p_physical_scancode, int p_unicode, boolean p_pressed);
 
 	/**
-	 * Forward game device's key events from the main thread to the GL thread.
+	 * Forward game device's key events.
 	 */
 	public static native void joybutton(int p_device, int p_but, boolean p_pressed);
 
 	/**
-	 * Forward joystick devices axis motion events from the main thread to the GL thread.
+	 * Forward joystick devices axis motion events.
 	 */
 	public static native void joyaxis(int p_device, int p_axis, float p_value);
 
 	/**
-	 * Forward joystick devices hat motion events from the main thread to the GL thread.
+	 * Forward joystick devices hat motion events.
 	 */
 	public static native void joyhat(int p_device, int p_hat_x, int p_hat_y);
 

+ 0 - 5
platform/android/java/lib/src/org/godotengine/godot/GodotView.java

@@ -31,7 +31,6 @@
 package org.godotengine.godot;
 
 import org.godotengine.godot.gl.GLSurfaceView;
-import org.godotengine.godot.input.GodotGestureHandler;
 import org.godotengine.godot.input.GodotInputHandler;
 import org.godotengine.godot.utils.GLUtils;
 import org.godotengine.godot.xr.XRMode;
@@ -45,7 +44,6 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.PixelFormat;
-import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -76,7 +74,6 @@ public class GodotView extends GLSurfaceView {
 
 	private final Godot godot;
 	private final GodotInputHandler inputHandler;
-	private final GestureDetector detector;
 	private final GodotRenderer godotRenderer;
 
 	private EGLConfigChooser eglConfigChooser;
@@ -90,7 +87,6 @@ public class GodotView extends GLSurfaceView {
 
 		this.godot = godot;
 		this.inputHandler = new GodotInputHandler(this);
-		this.detector = new GestureDetector(context, new GodotGestureHandler(this));
 		this.godotRenderer = new GodotRenderer();
 
 		init(xrMode, p_translucent);
@@ -104,7 +100,6 @@ public class GodotView extends GLSurfaceView {
 	@Override
 	public boolean onTouchEvent(MotionEvent event) {
 		super.onTouchEvent(event);
-		this.detector.onTouchEvent(event);
 		return inputHandler.onTouchEvent(event);
 	}
 

+ 0 - 87
platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java

@@ -1,87 +0,0 @@
-/*************************************************************************/
-/*  GodotGestureHandler.java                                             */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-package org.godotengine.godot.input;
-
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotView;
-
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-/**
- * Handles gesture input related events for the {@link GodotView} view.
- * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
- */
-public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
-	private final GodotView godotView;
-
-	public GodotGestureHandler(GodotView godotView) {
-		this.godotView = godotView;
-	}
-
-	private void queueEvent(Runnable task) {
-		godotView.queueEvent(task);
-	}
-
-	@Override
-	public boolean onDown(MotionEvent event) {
-		super.onDown(event);
-		//Log.i("GodotGesture", "onDown");
-		return true;
-	}
-
-	@Override
-	public boolean onSingleTapConfirmed(MotionEvent event) {
-		super.onSingleTapConfirmed(event);
-		return true;
-	}
-
-	@Override
-	public void onLongPress(MotionEvent event) {
-		//Log.i("GodotGesture", "onLongPress");
-	}
-
-	@Override
-	public boolean onDoubleTap(MotionEvent event) {
-		//Log.i("GodotGesture", "onDoubleTap");
-		final int x = Math.round(event.getX());
-		final int y = Math.round(event.getY());
-		final int buttonMask = event.getButtonState();
-		GodotLib.doubleTap(buttonMask, x, y);
-		return true;
-	}
-
-	@Override
-	public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
-		//Log.i("GodotGesture", "onFling");
-		return true;
-	}
-}

+ 225 - 0
platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt

@@ -0,0 +1,225 @@
+/*************************************************************************/
+/*  GodotGestureHandler.kt                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+package org.godotengine.godot.input
+
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import android.view.ScaleGestureDetector.OnScaleGestureListener
+import org.godotengine.godot.GodotLib
+
+/**
+ * Handles regular and scale gesture input related events for the [GodotView] view.
+ *
+ * @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
+ * @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
+ */
+internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
+
+	companion object {
+		private val TAG = GodotGestureHandler::class.java.simpleName
+	}
+
+	/**
+	 * Enable pan and scale gestures
+	 */
+	var panningAndScalingEnabled = false
+
+	private var doubleTapInProgress = false
+	private var dragInProgress = false
+	private var scaleInProgress = false
+	private var contextClickInProgress = false
+
+	override fun onDown(event: MotionEvent): Boolean {
+		// Don't send / register a down event while we're in the middle of a double-tap
+		if (!doubleTapInProgress) {
+			// Send the down event
+			GodotInputHandler.handleMotionEvent(event)
+		}
+		return true
+	}
+
+	override fun onSingleTapUp(event: MotionEvent): Boolean {
+		GodotInputHandler.handleMotionEvent(event)
+		return true
+	}
+
+	override fun onLongPress(event: MotionEvent) {
+		contextClickRouter(event)
+	}
+
+	private fun contextClickRouter(event: MotionEvent) {
+		if (scaleInProgress) {
+			return
+		}
+
+		// Cancel the previous down event
+		GodotInputHandler.handleMotionEvent(
+			event.source,
+			MotionEvent.ACTION_CANCEL,
+			event.buttonState,
+			event.x,
+			event.y
+		)
+
+		// Turn a context click into a single tap right mouse button click.
+		GodotInputHandler.handleMouseEvent(
+			MotionEvent.ACTION_DOWN,
+			MotionEvent.BUTTON_SECONDARY,
+			event.x,
+			event.y
+		)
+		contextClickInProgress = true
+	}
+
+	fun onMotionEvent(event: MotionEvent): Boolean {
+		return when (event.actionMasked) {
+			MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> {
+				onActionUp(event)
+			}
+			MotionEvent.ACTION_MOVE -> {
+				onActionMove(event)
+			}
+			else -> false
+		}
+	}
+
+	private fun onActionUp(event: MotionEvent): Boolean {
+		if (dragInProgress) {
+			GodotInputHandler.handleMotionEvent(event)
+			dragInProgress = false
+			return true
+		} else if (contextClickInProgress) {
+			GodotInputHandler.handleMouseEvent(
+				event.actionMasked,
+				0,
+				event.x,
+				event.y
+			)
+			contextClickInProgress = false
+			return true
+		}
+		return false
+	}
+
+	private fun onActionMove(event: MotionEvent): Boolean {
+		if (contextClickInProgress) {
+			GodotInputHandler.handleMouseEvent(
+				event.actionMasked,
+				MotionEvent.BUTTON_SECONDARY,
+				event.x,
+				event.y
+			)
+			return true
+		}
+		return false
+	}
+
+	override fun onDoubleTapEvent(event: MotionEvent): Boolean {
+		if (event.actionMasked == MotionEvent.ACTION_UP) {
+			doubleTapInProgress = false
+		}
+		return true
+	}
+
+	override fun onDoubleTap(event: MotionEvent): Boolean {
+		doubleTapInProgress = true
+		val x = event.x
+		val y = event.y
+		val buttonMask =
+			if (GodotInputHandler.isMouseEvent(event)) {
+				event.buttonState
+			} else {
+				MotionEvent.BUTTON_PRIMARY
+			}
+		GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true)
+		GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false)
+
+		return true
+	}
+
+	override fun onScroll(
+		originEvent: MotionEvent,
+		terminusEvent: MotionEvent,
+		distanceX: Float,
+		distanceY: Float
+	): Boolean {
+		if (scaleInProgress) {
+			if (dragInProgress) {
+				// Cancel the drag
+				GodotInputHandler.handleMotionEvent(
+					originEvent.source,
+					MotionEvent.ACTION_CANCEL,
+					originEvent.buttonState,
+					originEvent.x,
+					originEvent.y
+				)
+				dragInProgress = false
+			}
+			return true
+		}
+
+		dragInProgress = true
+
+		val x = terminusEvent.x
+		val y = terminusEvent.y
+		if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) {
+			GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
+		} else {
+			GodotInputHandler.handleMotionEvent(terminusEvent)
+		}
+		return true
+	}
+
+	override fun onScale(detector: ScaleGestureDetector?): Boolean {
+		if (detector == null || !panningAndScalingEnabled) {
+			return false
+		}
+		GodotLib.magnify(
+			detector.focusX,
+			detector.focusY,
+			detector.scaleFactor
+		)
+		return true
+	}
+
+	override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
+		if (detector == null || !panningAndScalingEnabled) {
+			return false
+		}
+		scaleInProgress = true
+		return true
+	}
+
+	override fun onScaleEnd(detector: ScaleGestureDetector?) {
+		scaleInProgress = false
+	}
+}

+ 161 - 91
platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java

@@ -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;
 	}

+ 17 - 26
platform/android/java_godot_lib_jni.cpp

@@ -281,56 +281,47 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
 	return should_swap_buffers;
 }
 
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click) {
+	if (step.get() <= 0) {
+		return;
+	}
+
+	input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click);
+}
+
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
 	if (step.get() <= 0) {
 		return;
 	}
 	Vector<AndroidInputHandler::TouchPos> points;
 	for (int i = 0; i < pointer_count; i++) {
 		jfloat p[3];
-		env->GetFloatArrayRegion(positions, i * 3, 3, p);
+		env->GetFloatArrayRegion(position, i * 3, 3, p);
 		AndroidInputHandler::TouchPos tp;
 		tp.pos = Point2(p[1], p[2]);
 		tp.id = (int)p[0];
 		points.push_back(tp);
 	}
 
-	if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
-		input_handler->process_mouse_event(ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
-	} else {
-		input_handler->process_touch(ev, pointer, points);
-	}
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
-	touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
-	touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
-	touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
+	input_handler->process_touch_event(ev, pointer, points);
 }
 
 // Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor) {
 	if (step.get() <= 0) {
 		return;
 	}
-	input_handler->process_hover(p_type, Point2(p_x, p_y));
+	input_handler->process_magnify(Point2(p_x, p_y), p_factor);
 }
 
 // Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y) {
 	if (step.get() <= 0) {
 		return;
 	}
-	input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
+	input_handler->process_pan(Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y));
 }
 
 // Called on the UI thread

+ 4 - 6
platform/android/java_godot_lib_jni.h

@@ -45,12 +45,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
 JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_scancode, jint p_physical_scancode, jint p_unicode, jboolean p_pressed);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);