summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/android_input_handler.cpp217
-rw-r--r--platform/android/android_input_handler.h22
-rw-r--r--platform/android/detect.py12
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/android/export/export_plugin.cpp15
-rw-r--r--platform/android/java/app/config.gradle26
-rw-r--r--platform/android/java/editor/build.gradle23
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml2
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt16
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt4
-rw-r--r--platform/android/java/lib/res/values/strings.xml2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java37
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java39
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java87
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt289
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java279
-rw-r--r--platform/android/java_godot_lib_jni.cpp57
-rw-r--r--platform/android/java_godot_lib_jni.h14
-rw-r--r--platform/android/java_godot_wrapper.cpp9
-rw-r--r--platform/android/java_godot_wrapper.h2
22 files changed, 818 insertions, 382 deletions
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 6427346365..454bcd2eda 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -118,20 +118,31 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Input::get_singleton()->parse_input_event(ev);
}
-void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector<AndroidInputHandler::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.instantiate();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(p_pressed);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->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.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->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++) {
@@ -140,18 +151,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.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(true);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->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;
@@ -180,18 +186,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.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->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++) {
@@ -229,88 +224,118 @@ 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(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
+ Ref<InputEventMouseButton> ev;
+ ev.instantiate();
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ } else {
+ ev->set_position(mouse_event_info.pos);
+ ev->set_global_position(mouse_event_info.pos);
+ hover_prev_pos = mouse_event_info.pos;
+ }
+ ev->set_pressed(p_pressed);
+ MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+
+ buttons_state = event_buttons_mask;
+
+ ev->set_button_index(_button_index_from_mask(changed_button_mask));
+ ev->set_button_mask(event_buttons_mask);
+ ev->set_double_click(p_double_click);
+ Input::get_singleton()->parse_input_event(ev);
+}
+
+void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
+ _parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
+ 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, bool p_source_mouse_relative) {
+ MouseButton 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.instantiate();
_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::get_singleton()->parse_input_event(ev);
- hover_prev_pos = p_pos;
+ hover_prev_pos = p_event_pos;
} break;
- }
-}
-void AndroidInputHandler::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
- MouseButton 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.instantiate();
- _set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
- ev->set_position(hover_prev_pos);
- ev->set_global_position(hover_prev_pos);
- }
- ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
- MouseButton changed_button_mask = MouseButton(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, p_source_mouse_relative);
+ } break;
- ev->set_button_index(_button_index_from_mask(changed_button_mask));
- ev->set_button_mask(event_buttons_mask);
- Input::get_singleton()->parse_input_event(ev);
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ _release_mouse_event_info(p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_MOVE: {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- ev->set_relative(event_pos - hover_prev_pos);
- hover_prev_pos = event_pos;
- } else {
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
- ev->set_relative(event_pos);
+ ev->set_relative(p_event_pos);
+ } else {
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
+ ev->set_relative(p_event_pos - hover_prev_pos);
+ mouse_event_info.pos = p_event_pos;
+ hover_prev_pos = p_event_pos;
}
ev->set_button_mask(event_buttons_mask);
Input::get_singleton()->parse_input_event(ev);
} break;
+
case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev;
ev.instantiate();
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
+ } else {
+ 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, MouseButton::WHEEL_UP, event_vertical_factor);
- } else if (event_vertical_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor);
+ if (p_delta.y > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, p_delta.y);
+ } else if (p_delta.y < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -p_delta.y);
}
- if (event_horizontal_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor);
- } else if (event_horizontal_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor);
+ if (p_delta.x > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, p_delta.x);
+ } else if (p_delta.x < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -p_delta.x);
}
} break;
}
@@ -329,18 +354,22 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co
Input::get_singleton()->parse_input_event(evdd);
}
-void AndroidInputHandler::process_double_tap(int event_android_button_mask, Point2 p_pos) {
- MouseButton event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
- Ref<InputEventMouseButton> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_pressed(event_button_mask != MouseButton::NONE);
- ev->set_button_index(_button_index_from_mask(event_button_mask));
- ev->set_button_mask(event_button_mask);
- ev->set_double_click(true);
- Input::get_singleton()->parse_input_event(ev);
+void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
+ Ref<InputEventMagnifyGesture> magnify_event;
+ magnify_event.instantiate();
+ _set_key_modifier_state(magnify_event);
+ magnify_event->set_position(p_pos);
+ magnify_event->set_factor(p_factor);
+ Input::get_singleton()->parse_input_event(magnify_event);
+}
+
+void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
+ Ref<InputEventPanGesture> pan_event;
+ pan_event.instantiate();
+ _set_key_modifier_state(pan_event);
+ pan_event->set_position(p_pos);
+ pan_event->set_delta(p_delta);
+ Input::get_singleton()->parse_input_event(pan_event);
}
MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 6dfab7def8..88490f0407 100644
--- a/platform/android/android_input_handler.h
+++ b/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,
@@ -68,6 +73,7 @@ private:
MouseButton buttons_state = MouseButton::NONE;
Vector<TouchPos> touch;
+ MouseEventInfo mouse_event_info;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
@@ -77,11 +83,19 @@ private:
void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
+ void _parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
+
+ void _release_mouse_event_info(bool p_source_mouse_relative = false);
+
+ void _parse_all_touch(bool p_pressed);
+
+ void _release_all_touch();
+
public:
- 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 input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
- 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, bool p_source_mouse_relative);
+ 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 process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
};
diff --git a/platform/android/detect.py b/platform/android/detect.py
index ad63821162..1d9bcdd932 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -47,6 +47,9 @@ def get_flags():
return [
("arch", "arm64"), # Default for convenience.
("tools", False),
+ # Benefits of LTO for Android (size, performance) haven't been clearly established yet.
+ # So for now we override the default value which may be set when using `production=yes`.
+ ("lto", "none"),
]
@@ -132,6 +135,15 @@ def configure(env):
env.Append(CPPDEFINES=["_DEBUG"])
env.Append(CPPFLAGS=["-UNDEBUG"])
+ # LTO
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
+
# Compiler configuration
env["SHLIBSUFFIX"] = ".so"
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 5bbe0ffab6..f4c4e985fe 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -36,6 +36,7 @@
#include "export_plugin.h"
void register_android_exporter() {
+#ifndef ANDROID_ENABLED
EDITOR_DEF("export/android/android_sdk_path", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF("export/android/debug_keystore", "");
@@ -47,6 +48,7 @@ void register_android_exporter() {
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false);
+#endif
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
EditorExport::get_singleton()->add_export_platform(exporter);
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 0f8ef3f7d6..e5656bd00b 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -569,16 +569,15 @@ bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, c
}
zip_fileinfo EditorExportPlatformAndroid::get_zip_fileinfo() {
- OS::Time time = OS::get_singleton()->get_time();
- OS::Date date = OS::get_singleton()->get_date();
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
zip_fileinfo zipfi;
- zipfi.tmz_date.tm_hour = time.hour;
- zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.minute;
- zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed
- zipfi.tmz_date.tm_sec = time.second;
- zipfi.tmz_date.tm_year = date.year;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // tm_mon is zero indexed
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
zipfi.external_fa = 0;
zipfi.internal_fa = 0;
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index fbd97fae0b..0346625e4b 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -127,16 +127,36 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
if (requiredKeys.empty) {
libraryVersionName = map.values().join(".")
try {
+ if (map.containsKey("status")) {
+ int statusCode = 0
+ String statusValue = map["status"]
+ if (statusValue == null) {
+ statusCode = 0
+ } else if (statusValue.startsWith("alpha")) {
+ statusCode = 1
+ } else if (statusValue.startsWith("beta")) {
+ statusCode = 2
+ } else if (statusValue.startsWith("rc")) {
+ statusCode = 3
+ } else if (statusValue.startsWith("stable")) {
+ statusCode = 4
+ } else {
+ statusCode = 0
+ }
+
+ libraryVersionCode = statusCode
+ }
+
if (map.containsKey("patch")) {
- libraryVersionCode = Integer.parseInt(map["patch"])
+ libraryVersionCode += Integer.parseInt(map["patch"]) * 10
}
if (map.containsKey("minor")) {
- libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 1000)
}
if (map.containsKey("major")) {
- libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 100000)
}
} catch (NumberFormatException ignore) {
libraryVersionCode = 1
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
index 729966ee69..9152492e9d 100644
--- a/platform/android/java/editor/build.gradle
+++ b/platform/android/java/editor/build.gradle
@@ -12,6 +12,25 @@ dependencies {
implementation "androidx.window:window:1.0.0"
}
+ext {
+ // Build number added as a suffix to the version code, and incremented for each build/upload to
+ // the Google Play store.
+ // This should be reset on each stable release of Godot.
+ editorBuildNumber = 0
+ // Value by which the Godot version code should be offset by to make room for the build number
+ editorBuildNumberOffset = 100
+}
+
+def generateVersionCode() {
+ int libraryVersionCode = getGodotLibraryVersionCode()
+ return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber
+}
+
+def generateVersionName() {
+ String libraryVersionName = getGodotLibraryVersionName()
+ return libraryVersionName + ".$editorBuildNumber"
+}
+
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
@@ -20,8 +39,8 @@ android {
defaultConfig {
// The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
applicationId "org.godotengine.editor.v4"
- versionCode getGodotLibraryVersionCode()
- versionName getGodotLibraryVersionName()
+ versionCode generateVersionCode()
+ versionName generateVersionName()
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index abf506a83c..6aa5f06f31 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -7,7 +7,7 @@
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
- android:smallScreens="true"
+ android:smallScreens="false"
android:xlargeScreens="true" />
<uses-feature
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 740f3f48d3..489a81fc1a 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -77,6 +77,12 @@ open class GodotEditor : FullScreenGodotApp() {
}
super.onCreate(savedInstanceState)
+
+ // Enable long press, panning and scaling gestures
+ godotFragment?.renderView?.inputHandler?.apply {
+ enableLongPress(enableLongPressGestures())
+ enablePanningAndScalingGestures(enablePanAndScaleGestures())
+ }
}
private fun updateCommandLineParams(args: Array<String>?) {
@@ -148,6 +154,16 @@ open class GodotEditor : FullScreenGodotApp() {
*/
protected open fun overrideOrientationRequest() = true
+ /**
+ * Enable long press gestures for the Godot Android editor.
+ */
+ protected open fun enableLongPressGestures() = true
+
+ /**
+ * Enable pan and scale gestures for the Godot Android editor.
+ */
+ protected open fun enablePanAndScaleGestures() = true
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index 783095f93a..b9536a7066 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -35,4 +35,8 @@ package org.godotengine.editor
*/
class GodotGame : GodotEditor() {
override fun overrideOrientationRequest() = false
+
+ override fun enableLongPressGestures() = false
+
+ override fun enablePanAndScaleGestures() = false
}
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index 010006b81e..f5a4ab1071 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -12,6 +12,8 @@
<string name="text_button_resume">Resume Download</string>
<string name="text_button_cancel">Cancel</string>
<string name="text_button_cancel_verify">Cancel Verification</string>
+ <string name="text_error_title">Error!</string>
+ <string name="error_engine_setup_message">Unable to setup the Godot Engine! Aborting…</string>
<!-- APK Expansion Strings -->
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 28e689e63a..a75c69484c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -57,6 +57,7 @@ import android.content.SharedPreferences.Editor;
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Sensor;
@@ -69,6 +70,7 @@ import android.os.Environment;
import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
@@ -85,6 +87,8 @@ import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
@@ -105,6 +109,8 @@ import java.util.List;
import java.util.Locale;
public class Godot extends Fragment implements SensorEventListener, IDownloaderClient {
+ private static final String TAG = Godot.class.getSimpleName();
+
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -250,7 +256,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
*/
@Keep
- private void onVideoInit() {
+ private boolean onVideoInit() {
final Activity activity = getActivity();
containerLayout = new FrameLayout(activity);
containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
@@ -262,7 +268,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
containerLayout.addView(editText);
- GodotLib.setup(command_line);
+ if (!GodotLib.setup(command_line)) {
+ Log.e(TAG, "Unable to setup the Godot engine! Aborting...");
+ alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit);
+ return false;
+ }
final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name");
if (videoDriver.equals("vulkan")) {
@@ -303,6 +313,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
}
+ return true;
}
public void setKeepScreenOn(final boolean p_enabled) {
@@ -344,13 +355,27 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void alert(final String message, final String title) {
+ alert(message, title, null);
+ }
+
+ private void alert(@StringRes int messageResId, @StringRes int titleResId, @Nullable Runnable okCallback) {
+ Resources res = getResources();
+ alert(res.getString(messageResId), res.getString(titleResId), okCallback);
+ }
+
+ private void alert(final String message, final String title, @Nullable Runnable okCallback) {
final Activity activity = getActivity();
runOnUiThread(() -> {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(message).setTitle(title);
builder.setPositiveButton(
"OK",
- (dialog, id) -> dialog.cancel());
+ (dialog, id) -> {
+ if (okCallback != null) {
+ okCallback.run();
+ }
+ dialog.cancel();
+ });
AlertDialog dialog = builder.create();
dialog.show();
});
@@ -471,7 +496,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
- GodotLib.initialize(activity,
+ godot_initialized = GodotLib.initialize(activity,
this,
activity.getAssets(),
io,
@@ -482,8 +507,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
tts);
result_callback = null;
-
- godot_initialized = true;
}
@Override
@@ -1023,7 +1046,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
- private GodotRenderView getRenderView() { // used by native side to get renderView
+ public GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 08da1b1832..513021f1d1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -31,7 +31,6 @@
package org.godotengine.godot;
import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.gl.GodotRenderer;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
import org.godotengine.godot.xr.XRMode;
@@ -46,7 +45,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -75,7 +73,6 @@ import androidx.annotation.Keep;
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler inputHandler;
- private final GestureDetector detector;
private final GodotRenderer godotRenderer;
private PointerIcon pointerIcon;
@@ -85,7 +82,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
this.godot = godot;
this.inputHandler = new GodotInputHandler(this);
- this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@@ -132,7 +128,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- this.detector.onTouchEvent(event);
return inputHandler.onTouchEvent(event);
}
@@ -156,6 +151,24 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ inputHandler.onPointerCaptureChange(hasCapture);
+ }
+
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ inputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ inputHandler.onPointerCaptureChange(false);
+ }
+
/**
* called from JNI to change pointer icon
*/
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index f855fc6cf6..26aad867b1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -54,7 +54,7 @@ public class GodotLib {
/**
* Invoked on the main thread to initialize Godot native layer.
*/
- public static native void initialize(Activity activity,
+ public static native boolean initialize(Activity activity,
Godot p_instance,
AssetManager p_asset_manager,
GodotIO godotIO,
@@ -74,7 +74,7 @@ public class GodotLib {
* Invoked on the GL thread to complete setup for the Godot native layer logic.
* @param p_cmdline Command line arguments used to configure Godot native layer components.
*/
- public static native void setup(String[] p_cmdline);
+ public static native boolean setup(String[] p_cmdline);
/**
* Invoked on the GL thread when the underlying Android surface has changed size.
@@ -92,7 +92,7 @@ public class GodotLib {
public static native void newcontext(Surface p_surface);
/**
- * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
+ * Forward {@link Activity#onBackPressed()} event.
*/
public static native void back();
@@ -108,63 +108,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, boolean sourceMouseRelative);
- /**
- * 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_keycode, int p_physical_keycode, 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);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index c386a2d2eb..fa6c3280b9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -30,7 +30,6 @@
package org.godotengine.godot;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView;
@@ -38,7 +37,6 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -49,7 +47,6 @@ import androidx.annotation.Keep;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler mInputHandler;
- private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer;
private PointerIcon pointerIcon;
@@ -58,7 +55,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
this.godot = godot;
mInputHandler = new GodotInputHandler(this);
- mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@@ -106,7 +102,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
return mInputHandler.onTouchEvent(event);
}
@@ -130,6 +125,24 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
return mInputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ mInputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ mInputHandler.onPointerCaptureChange(false);
+ }
+
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ mInputHandler.onPointerCaptureChange(hasCapture);
+ }
+
/**
* called from JNI to change pointer icon
*/
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
deleted file mode 100644
index 778efa914a..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ /dev/null
@@ -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.GodotRenderView;
-
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-/**
- * Handles gesture input related events for the {@link GodotRenderView} view.
- * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
- */
-public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
- private final GodotRenderView mRenderView;
-
- public GodotGestureHandler(GodotRenderView godotView) {
- mRenderView = godotView;
- }
-
- private void queueEvent(Runnable task) {
- mRenderView.queueOnRenderThread(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;
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
new file mode 100644
index 0000000000..9715c31fc1
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -0,0 +1,289 @@
+/*************************************************************************/
+/* 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.os.Build
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.InputDevice
+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
+ private var pointerCaptureInProgress = 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 onPointerCaptureChange(hasCapture: Boolean) {
+ if (pointerCaptureInProgress == hasCapture) {
+ return
+ }
+
+ if (!hasCapture) {
+ // Dispatch a mouse relative ACTION_UP event to signal the end of the capture
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ 0,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ false,
+ true
+ )
+ }
+ pointerCaptureInProgress = hasCapture
+ }
+
+ 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 {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ when {
+ pointerCaptureInProgress -> {
+ return if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
+ // Don't dispatch the ACTION_CANCEL while a capture is in progress
+ true
+ } else {
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ event.buttonState,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ pointerCaptureInProgress = false
+ true
+ }
+ }
+ dragInProgress -> {
+ GodotInputHandler.handleMotionEvent(event)
+ dragInProgress = false
+ return true
+ }
+ contextClickInProgress -> {
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ 0,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ contextClickInProgress = false
+ return true
+ }
+ else -> return false
+ }
+ }
+
+ private fun onActionMove(event: MotionEvent): Boolean {
+ if (contextClickInProgress) {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ MotionEvent.BUTTON_SECONDARY,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ 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
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index da15b2490c..03cb8034fa 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/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 GodotRenderView} view.
*/
public class GodotInputHandler implements InputManager.InputDeviceListener {
- private final GodotRenderView mRenderView;
- private final InputManager mInputManager;
-
- 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 GodotRenderView mRenderView;
+ private final InputManager mInputManager;
+ private final GestureDetector gestureDetector;
+ private final ScaleGestureDetector scaleGestureDetector;
+ private final GodotGestureHandler godotGestureHandler;
+
public GodotInputHandler(GodotRenderView godotView) {
+ final Context context = godotView.getView().getContext();
mRenderView = godotView;
- mInputManager = (InputManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_SERVICE);
+ mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.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;
@@ -77,6 +105,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 void onPointerCaptureChange(boolean hasCapture) {
+ godotGestureHandler.onPointerCaptureChange(hasCapture);
+ }
+
public boolean onKeyUp(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
@@ -87,7 +119,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) {
@@ -121,11 +153,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;
@@ -145,47 +176,41 @@ 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 (mRenderView != 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 (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler 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) {
@@ -198,15 +223,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);
}
}
@@ -221,17 +245,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) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return handleMouseEvent(event);
- }
+ } else if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
}
return false;
@@ -243,7 +258,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int deviceId : deviceIds) {
InputDevice device = mInputManager.getInputDevice(deviceId);
if (DEBUG) {
- Log.v("GodotInputHandler", 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 +303,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 +320,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 +352,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,39 +417,113 @@ 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) {
+ boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return mouseSource;
+ }
+
+ 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, 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);
+ boolean sourceMouseRelative = false;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+ 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);
- 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 true;
- }
+ case MotionEvent.ACTION_MOVE:
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);
+ GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative);
+ return true;
}
+ }
+ 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_UP: {
- // we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE
+ 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;
}
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 422c05e5ce..04b69d5b86 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -79,7 +79,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
JavaVM *jvm;
env->GetJavaVM(&jvm);
@@ -100,7 +100,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
- godot_java->on_video_init(env);
+ return godot_java->on_video_init(env);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
@@ -123,7 +123,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
setup_android_thread();
const char **cmdline = nullptr;
@@ -133,10 +133,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
cmdlen = env->GetArrayLength(p_cmdline);
if (cmdlen) {
cmdline = (const char **)memalloc((cmdlen + 1) * sizeof(const char *));
- ERR_FAIL_NULL_MSG(cmdline, "Out of memory.");
+ ERR_FAIL_NULL_V_MSG(cmdline, false, "Out of memory.");
cmdline[cmdlen] = nullptr;
j_cmdline = (jstring *)memalloc(cmdlen * sizeof(jstring));
- ERR_FAIL_NULL_MSG(j_cmdline, "Out of memory.");
+ ERR_FAIL_NULL_V_MSG(j_cmdline, false, "Out of memory.");
for (int i = 0; i < cmdlen; i++) {
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
@@ -161,11 +161,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
// Note: --help and --version return ERR_HELP, but this should be translated to 0 if exit codes are propagated.
if (err != OK) {
- return; // should exit instead and print the error
+ return false;
}
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
GDREGISTER_CLASS(JNISingleton);
+ return true;
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height) {
@@ -254,7 +255,17 @@ 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, jboolean p_source_mouse_relative) {
+ 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, p_source_mouse_relative);
+}
+
+// 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;
}
@@ -262,50 +273,30 @@ void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev,
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_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
- input_handler->process_mouse_event(input_device, 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);
+ input_handler->process_touch_event(ev, pointer, points);
}
// 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);
-}
-
-// 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
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 3c48ca0459..09fed15690 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,20 +37,18 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
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, jboolean p_source_mouse_relative);
+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_keycode, jint p_physical_keycode, 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);
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index e3456fe4e4..07b0d75921 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -58,7 +58,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
}
// get some Godot method pointers...
- _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
+ _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
_finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
@@ -125,14 +125,15 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
return _godot_view;
}
-void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
+bool GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
- ERR_FAIL_NULL(p_env);
- p_env->CallVoidMethod(godot_instance, _on_video_init);
+ ERR_FAIL_NULL_V(p_env, false);
+ return p_env->CallBooleanMethod(godot_instance, _on_video_init);
}
+ return false;
}
void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index bbf7c0ae33..a6c7853107 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -83,7 +83,7 @@ public:
jobject get_class_loader();
GodotJavaViewWrapper *get_godot_view();
- void on_video_init(JNIEnv *p_env = nullptr);
+ bool on_video_init(JNIEnv *p_env = nullptr);
void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);