Browse Source

Improve touchpad and mouse support for the Android editor

- Fix issues with using a touchpad to click, drag, interact with the navigation controls, etc..
- Fix issues with panning with 2+ fingers
- Fix issues with using double-tap to magnify on the spatial editor
Fredia Huya-Kouadio 2 years ago
parent
commit
01ee00f710

+ 4 - 2
platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java

@@ -166,8 +166,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
 
 	@Override
 	public void requestPointerCapture() {
-		super.requestPointerCapture();
-		inputHandler.onPointerCaptureChange(true);
+		if (canCapturePointer()) {
+			super.requestPointerCapture();
+			inputHandler.onPointerCaptureChange(true);
+		}
 	}
 
 	@Override

+ 4 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java

@@ -51,4 +51,8 @@ public interface GodotRenderView {
 	void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
 
 	void setPointerIcon(int pointerType);
+
+	default boolean canCapturePointer() {
+		return getInputHandler().canCapturePointer();
+	}
 }

+ 4 - 2
platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java

@@ -134,8 +134,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
 
 	@Override
 	public void requestPointerCapture() {
-		super.requestPointerCapture();
-		mInputHandler.onPointerCaptureChange(true);
+		if (canCapturePointer()) {
+			super.requestPointerCapture();
+			mInputHandler.onPointerCaptureChange(true);
+		}
 	}
 
 	@Override

+ 10 - 9
platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt

@@ -227,16 +227,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
 				)
 				dragInProgress = false
 			}
-			return true
 		}
 
-		dragInProgress = true
-
 		val x = terminusEvent.x
 		val y = terminusEvent.y
 		if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) {
 			GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
-		} else {
+		} else if (!scaleInProgress){
+			dragInProgress = true
 			GodotInputHandler.handleMotionEvent(terminusEvent)
 		}
 		return true
@@ -246,11 +244,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
 		if (!panningAndScalingEnabled || pointerCaptureInProgress) {
 			return false
 		}
-		GodotLib.magnify(
-			detector.focusX,
-			detector.focusY,
-			detector.scaleFactor
-		)
+
+		if (detector.scaleFactor >= 0.8f && detector.scaleFactor != 1f && detector.scaleFactor <= 1.2f) {
+			GodotLib.magnify(
+					detector.focusX,
+					detector.focusY,
+					detector.scaleFactor
+			)
+		}
 		return true
 	}
 

+ 28 - 3
platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java

@@ -66,6 +66,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
 	private final ScaleGestureDetector scaleGestureDetector;
 	private final GodotGestureHandler godotGestureHandler;
 
+	/**
+	 * Used to decide whether mouse capture can be enabled.
+	 */
+	private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
 	public GodotInputHandler(GodotRenderView godotView) {
 		final Context context = godotView.getView().getContext();
 		mRenderView = godotView;
@@ -105,6 +110,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
 		return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
 	}
 
+	public boolean canCapturePointer() {
+		return lastSeenToolType == MotionEvent.TOOL_TYPE_MOUSE;
+	}
+
 	public void onPointerCaptureChange(boolean hasCapture) {
 		godotGestureHandler.onPointerCaptureChange(hasCapture);
 	}
@@ -174,6 +183,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
 	}
 
 	public boolean onTouchEvent(final MotionEvent event) {
+		lastSeenToolType = event.getToolType(0);
+
 		this.scaleGestureDetector.onTouchEvent(event);
 		if (this.gestureDetector.onTouchEvent(event)) {
 			// The gesture detector has handled the event.
@@ -198,6 +209,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
 	}
 
 	public boolean onGenericMotionEvent(MotionEvent event) {
+		lastSeenToolType = event.getToolType(0);
+
 		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
 			// The gesture detector has handled the event.
 			return true;
@@ -471,15 +484,27 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
 	}
 
 	static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+		// Fix the buttonsMask
+		switch (eventAction) {
+			case MotionEvent.ACTION_CANCEL:
+			case MotionEvent.ACTION_UP:
+				// Zero-up the button state
+				buttonsMask = 0;
+				break;
+			case MotionEvent.ACTION_DOWN:
+			case MotionEvent.ACTION_MOVE:
+				if (buttonsMask == 0) {
+					buttonsMask = MotionEvent.BUTTON_PRIMARY;
+				}
+				break;
+		}
+
 		// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
 		// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
 		// stream of events to the engine.
 		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:

+ 12 - 1
platform/android/java_godot_view_wrapper.cpp

@@ -49,6 +49,8 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
 		_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
 		_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
 	}
+
+	_can_capture_pointer = env->GetMethodID(_cls, "canCapturePointer", "()Z");
 }
 
 bool GodotJavaViewWrapper::can_update_pointer_icon() const {
@@ -56,7 +58,16 @@ bool GodotJavaViewWrapper::can_update_pointer_icon() const {
 }
 
 bool GodotJavaViewWrapper::can_capture_pointer() const {
-	return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr;
+	// We can capture the pointer if the other jni capture method ids are initialized,
+	// and GodotView#canCapturePointer() returns true.
+	if (_request_pointer_capture != nullptr && _release_pointer_capture != nullptr && _can_capture_pointer != nullptr) {
+		JNIEnv *env = get_jni_env();
+		ERR_FAIL_NULL_V(env, false);
+
+		return env->CallBooleanMethod(_godot_view, _can_capture_pointer);
+	}
+
+	return false;
 }
 
 void GodotJavaViewWrapper::request_pointer_capture() {

+ 1 - 0
platform/android/java_godot_view_wrapper.h

@@ -44,6 +44,7 @@ private:
 
 	jobject _godot_view;
 
+	jmethodID _can_capture_pointer = 0;
 	jmethodID _request_pointer_capture = 0;
 	jmethodID _release_pointer_capture = 0;