diff options
Diffstat (limited to 'platform/android')
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); |