summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/SCsub13
-rw-r--r--platform/android/android_input_handler.cpp14
-rw-r--r--platform/android/android_input_handler.h6
-rw-r--r--platform/android/android_keys_utils.cpp4
-rw-r--r--platform/android/android_keys_utils.h4
-rw-r--r--platform/android/api/api.cpp8
-rw-r--r--platform/android/api/api.h4
-rw-r--r--platform/android/api/java_class_wrapper.h14
-rw-r--r--platform/android/api/jni_singleton.h38
-rw-r--r--platform/android/audio_driver_opensl.cpp27
-rw-r--r--platform/android/audio_driver_opensl.h6
-rw-r--r--platform/android/detect.py7
-rw-r--r--platform/android/dir_access_jandroid.cpp68
-rw-r--r--platform/android/dir_access_jandroid.h12
-rw-r--r--platform/android/display_server_android.cpp126
-rw-r--r--platform/android/display_server_android.h26
-rw-r--r--platform/android/export/export.cpp12
-rw-r--r--platform/android/export/export.h4
-rw-r--r--platform/android/export/export_plugin.cpp192
-rw-r--r--platform/android/export/export_plugin.h35
-rw-r--r--platform/android/export/godot_plugin_config.cpp5
-rw-r--r--platform/android/export/godot_plugin_config.h6
-rw-r--r--platform/android/export/gradle_export_util.cpp61
-rw-r--r--platform/android/export/gradle_export_util.h8
-rw-r--r--platform/android/file_access_android.cpp59
-rw-r--r--platform/android/file_access_android.h36
-rw-r--r--platform/android/java/app/AndroidManifest.xml18
-rw-r--r--platform/android/java/app/build.gradle47
-rw-r--r--platform/android/java/app/config.gradle70
-rw-r--r--platform/android/java/app/res/values/themes.xml4
-rw-r--r--platform/android/java/app/settings.gradle13
-rw-r--r--platform/android/java/app/src/com/godot/game/GodotApp.java4
-rw-r--r--platform/android/java/build.gradle163
-rw-r--r--platform/android/java/editor/build.gradle83
-rw-r--r--platform/android/java/editor/src/dev/res/values/strings.xml4
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml71
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java151
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java (renamed from platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java)30
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java41
-rw-r--r--platform/android/java/editor/src/main/res/values/dimens.xml5
-rw-r--r--platform/android/java/editor/src/main/res/values/strings.xml4
-rw-r--r--platform/android/java/lib/AndroidManifest.xml13
-rw-r--r--platform/android/java/lib/build.gradle109
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Dictionary.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java28
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java194
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java61
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java17
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java566
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java1939
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java (renamed from platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java)26
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java13
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java134
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java102
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java298
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/tts/GodotUtterance.java55
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java141
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java6
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt5
-rw-r--r--platform/android/java/nativeSrcsConfigs/build.gradle8
-rw-r--r--platform/android/java/scripts/publish-module.gradle69
-rw-r--r--platform/android/java/scripts/publish-root.gradle39
-rw-r--r--platform/android/java/settings.gradle16
-rw-r--r--platform/android/java_class_wrapper.cpp128
-rw-r--r--platform/android/java_godot_io_wrapper.cpp99
-rw-r--r--platform/android/java_godot_io_wrapper.h18
-rw-r--r--platform/android/java_godot_lib_jni.cpp120
-rw-r--r--platform/android/java_godot_lib_jni.h9
-rw-r--r--platform/android/java_godot_view_wrapper.cpp20
-rw-r--r--platform/android/java_godot_view_wrapper.h6
-rw-r--r--platform/android/java_godot_wrapper.cpp112
-rw-r--r--platform/android/java_godot_wrapper.h50
-rw-r--r--platform/android/jni_utils.cpp58
-rw-r--r--platform/android/jni_utils.h4
-rw-r--r--platform/android/net_socket_android.cpp33
-rw-r--r--platform/android/net_socket_android.h6
-rw-r--r--platform/android/os_android.cpp116
-rw-r--r--platform/android/os_android.h27
-rw-r--r--platform/android/platform_config.h4
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp13
-rw-r--r--platform/android/plugin/godot_plugin_jni.h4
-rw-r--r--platform/android/string_android.h5
-rw-r--r--platform/android/thread_jandroid.cpp4
-rw-r--r--platform/android/thread_jandroid.h6
-rw-r--r--platform/android/tts_android.cpp189
-rw-r--r--platform/android/tts_android.h67
-rw-r--r--platform/android/vulkan/vulkan_context_android.cpp4
-rw-r--r--platform/android/vulkan/vulkan_context_android.h4
116 files changed, 5398 insertions, 1203 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index ecc72019e5..ad226255bc 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -8,6 +8,7 @@ android_files = [
"file_access_android.cpp",
"audio_driver_opensl.cpp",
"dir_access_jandroid.cpp",
+ "tts_android.cpp",
"thread_jandroid.cpp",
"net_socket_android.cpp",
"java_godot_lib_jni.cpp",
@@ -18,6 +19,7 @@ android_files = [
"jni_utils.cpp",
"android_keys_utils.cpp",
"display_server_android.cpp",
+ "plugin/godot_plugin_jni.cpp",
"vulkan/vulkan_context_android.cpp",
]
@@ -52,10 +54,17 @@ else:
if lib_arch_dir != "":
if env["target"] == "release":
lib_type_dir = "release"
- else: # release_debug, debug
+ elif env["target"] == "release_debug":
lib_type_dir = "debug"
+ else: # debug
+ lib_type_dir = "dev"
- out_dir = "#platform/android/java/lib/libs/" + lib_type_dir + "/" + lib_arch_dir
+ if env["tools"]:
+ lib_tools_dir = "tools/"
+ else:
+ lib_tools_dir = ""
+
+ out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
env_android.Command(
out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
)
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 52f80b3080..10f23b320b 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,10 +39,7 @@ void AndroidInputHandler::process_joy_event(AndroidInputHandler::JoypadEvent p_e
Input::get_singleton()->joy_button(p_event.device, (JoyButton)p_event.index, p_event.pressed);
break;
case JOY_EVENT_AXIS:
- Input::JoyAxisValue value;
- value.min = -1;
- value.value = p_event.value;
- Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, value);
+ Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, p_event.value);
break;
case JOY_EVENT_HAT:
Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
@@ -168,8 +165,9 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
ERR_CONTINUE(idx == -1);
- if (touch[i].pos == p_points[idx].pos)
- continue; //no move unncesearily
+ if (touch[i].pos == p_points[idx].pos) {
+ continue; // Don't move unnecessarily.
+ }
Ref<InputEventScreenDrag> ev;
ev.instantiate();
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index e0b4196f14..e9c0ec1475 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -88,4 +88,4 @@ public:
void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
};
-#endif
+#endif // ANDROID_INPUT_HANDLER_H
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index 0cea0589ce..885e4ff145 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index bac9bab6db..24a6589fba 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp
index 03355e4815..f80f1e3051 100644
--- a/platform/android/api/api.cpp
+++ b/platform/android/api/api.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -64,14 +64,14 @@ void JavaClassWrapper::_bind_methods() {
#if !defined(ANDROID_ENABLED)
-Variant JavaClass::call(const StringName &, const Variant **, int, Callable::CallError &) {
+Variant JavaClass::callp(const StringName &, const Variant **, int, Callable::CallError &) {
return Variant();
}
JavaClass::JavaClass() {
}
-Variant JavaObject::call(const StringName &, const Variant **, int, Callable::CallError &) {
+Variant JavaObject::callp(const StringName &, const Variant **, int, Callable::CallError &) {
return Variant();
}
diff --git a/platform/android/api/api.h b/platform/android/api/api.h
index fe3a6734ac..a4ee27cf81 100644
--- a/platform/android/api/api.h
+++ b/platform/android/api/api.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index ff7bf43573..ac8d6585d3 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -63,7 +63,7 @@ class JavaClass : public RefCounted {
ARG_TYPE_MASK = (1 << 16) - 1
};
- Map<StringName, Variant> constant_map;
+ RBMap<StringName, Variant> constant_map;
struct MethodInfo {
bool _static = false;
@@ -174,12 +174,12 @@ class JavaClass : public RefCounted {
bool _call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret);
friend class JavaClassWrapper;
- Map<StringName, List<MethodInfo>> methods;
+ HashMap<StringName, List<MethodInfo>> methods;
jclass _class;
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
JavaClass();
};
@@ -195,7 +195,7 @@ class JavaObject : public RefCounted {
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
#ifdef ANDROID_ENABLED
JavaObject(const Ref<JavaClass> &p_base, jobject *p_instance);
@@ -207,7 +207,7 @@ class JavaClassWrapper : public Object {
GDCLASS(JavaClassWrapper, Object);
#ifdef ANDROID_ENABLED
- Map<String, Ref<JavaClass>> class_cache;
+ RBMap<String, Ref<JavaClass>> class_cache;
friend class JavaClass;
jclass activityClass;
jmethodID findClass;
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h
index 965eaabf81..690fddae21 100644
--- a/platform/android/api/jni_singleton.h
+++ b/platform/android/api/jni_singleton.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,10 @@
#ifndef JNI_SINGLETON_H
#define JNI_SINGLETON_H
-#include <core/config/engine.h>
-#include <core/variant/variant.h>
+#include "core/config/engine.h"
+#include "core/variant/variant.h"
#ifdef ANDROID_ENABLED
-#include <platform/android/jni_utils.h>
+#include "platform/android/jni_utils.h"
#endif
class JNISingleton : public Object {
@@ -48,13 +48,13 @@ class JNISingleton : public Object {
};
jobject instance;
- Map<StringName, MethodData> method_map;
+ RBMap<StringName, MethodData> method_map;
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
#ifdef ANDROID_ENABLED
- Map<StringName, MethodData>::Element *E = method_map.find(p_method);
+ RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
// Check the method we're looking for is in the JNISingleton map and that
// the arguments match.
@@ -70,10 +70,10 @@ public:
if (call_error) {
// The method is not in this map, defaulting to the regular instance calls.
- return Object::call(p_method, p_args, p_argcount, r_error);
+ return Object::callp(p_method, p_args, p_argcount, r_error);
}
- ERR_FAIL_COND_V(!instance, Variant());
+ ERR_FAIL_NULL_V(instance, Variant());
r_error.error = Callable::CallError::CALL_OK;
@@ -93,8 +93,9 @@ public:
for (int i = 0; i < p_argcount; i++) {
jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
v[i] = vr.val;
- if (vr.obj)
+ if (vr.obj) {
to_erase.push_back(vr.obj);
+ }
}
Variant ret;
@@ -175,7 +176,7 @@ public:
#else // ANDROID_ENABLED
// Defaulting to the regular instance calls.
- return Object::call(p_method, p_args, p_argcount, r_error);
+ return Object::callp(p_method, p_args, p_argcount, r_error);
#endif
}
@@ -197,18 +198,19 @@ public:
}
void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
- if (p_args.size() == 0)
+ if (p_args.size() == 0) {
ADD_SIGNAL(MethodInfo(p_name));
- else if (p_args.size() == 1)
+ } else if (p_args.size() == 1) {
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1")));
- else if (p_args.size() == 2)
+ } else if (p_args.size() == 2) {
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2")));
- else if (p_args.size() == 3)
+ } else if (p_args.size() == 3) {
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3")));
- else if (p_args.size() == 4)
+ } else if (p_args.size() == 4) {
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4")));
- else if (p_args.size() == 5)
+ } else if (p_args.size() == 5) {
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5")));
+ }
}
#endif
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index a1d8fb4810..6b22a0ffa1 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -56,8 +56,9 @@ void AudioDriverOpenSL::_buffer_callback(
}
}
- if (mix)
+ if (mix) {
mutex.unlock();
+ }
const int32_t *src_buff = mixdown_buffer;
@@ -74,13 +75,11 @@ void AudioDriverOpenSL::_buffer_callback(
void AudioDriverOpenSL::_buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
void *pContext) {
- AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
+ AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
ad->_buffer_callback(queueItf);
}
-AudioDriverOpenSL *AudioDriverOpenSL::s_ad = nullptr;
-
const char *AudioDriverOpenSL::get_name() const {
return "Android";
}
@@ -132,8 +131,6 @@ void AudioDriverOpenSL::start() {
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, BUFFER_COUNT };
- //bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
- //bufferQueue.numBuffers = BUFFER_COUNT; /* Four buffers in our buffer queue */
/* Setup the format of the content in the buffer queue */
pcm.formatType = SL_DATAFORMAT_PCM;
pcm.numChannels = 2;
@@ -154,13 +151,8 @@ void AudioDriverOpenSL::start() {
locator_outputmix.outputMix = OutputMix;
audioSink.pLocator = (void *)&locator_outputmix;
audioSink.pFormat = nullptr;
- /* Initialize the context for Buffer queue callbacks */
- //cntxt.pDataBase = (void*)&pcmData;
- //cntxt.pData = cntxt.pDataBase;
- //cntxt.size = sizeof(pcmData);
/* Create the music player */
-
{
const SLInterfaceID ids[2] = { SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND };
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
@@ -207,7 +199,7 @@ void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf qu
}
void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {
- AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
+ AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
ad->_record_buffer_callback(queueItf);
}
@@ -312,13 +304,15 @@ AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const {
}
void AudioDriverOpenSL::lock() {
- if (active)
+ if (active) {
mutex.lock();
+ }
}
void AudioDriverOpenSL::unlock() {
- if (active)
+ if (active) {
mutex.unlock();
+ }
}
void AudioDriverOpenSL::finish() {
@@ -338,5 +332,4 @@ void AudioDriverOpenSL::set_pause(bool p_pause) {
}
AudioDriverOpenSL::AudioDriverOpenSL() {
- s_ad = this;
}
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index fcc2513f3f..7b09438858 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -105,4 +105,4 @@ public:
AudioDriverOpenSL();
};
-#endif // AUDIO_DRIVER_ANDROID_H
+#endif // AUDIO_DRIVER_OPENSL_H
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 3319d5890c..0099ac7e0d 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -188,8 +188,11 @@ def configure(env):
if env["target"].startswith("release"):
if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(LINKFLAGS=["-O2"])
- env.Append(CCFLAGS=["-O2", "-fomit-frame-pointer"])
+ # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
+ # when using `target=release_debug`.
+ opt = "-O3" if env["target"] == "release" else "-O2"
+ env.Append(LINKFLAGS=[opt])
+ env.Append(CCFLAGS=[opt, "-fomit-frame-pointer"])
elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["-Os"])
env.Append(LINKFLAGS=["-Os"])
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index d2726b8652..5b9eee8117 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "dir_access_jandroid.h"
+
#include "core/string/print_string.h"
#include "file_access_android.h"
#include "string_android.h"
@@ -41,7 +42,7 @@ jmethodID DirAccessJAndroid::_dir_next = nullptr;
jmethodID DirAccessJAndroid::_dir_close = nullptr;
jmethodID DirAccessJAndroid::_dir_is_dir = nullptr;
-DirAccess *DirAccessJAndroid::create_fs() {
+Ref<DirAccess> DirAccessJAndroid::create_fs() {
return memnew(DirAccessJAndroid);
}
@@ -51,9 +52,9 @@ Error DirAccessJAndroid::list_dir_begin() {
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
int res = env->CallIntMethod(io, _dir_open, js);
- if (res <= 0)
+ if (res <= 0) {
return ERR_CANT_OPEN;
-
+ }
id = res;
return OK;
@@ -64,9 +65,9 @@ String DirAccessJAndroid::get_next() {
JNIEnv *env = get_jni_env();
jstring str = (jstring)env->CallObjectMethod(io, _dir_next, id);
- if (!str)
+ if (!str) {
return "";
-
+ }
String ret = jstring_to_string((jstring)str, env);
env->DeleteLocalRef((jobject)str);
return ret;
@@ -83,9 +84,9 @@ bool DirAccessJAndroid::current_is_hidden() const {
}
void DirAccessJAndroid::list_dir_end() {
- if (id == 0)
+ if (id == 0) {
return;
-
+ }
JNIEnv *env = get_jni_env();
env->CallVoidMethod(io, _dir_close, id);
id = 0;
@@ -102,22 +103,25 @@ String DirAccessJAndroid::get_drive(int p_drive) {
Error DirAccessJAndroid::change_dir(String p_dir) {
JNIEnv *env = get_jni_env();
- if (p_dir.is_empty() || p_dir == "." || (p_dir == ".." && current_dir.is_empty()))
+ if (p_dir.is_empty() || p_dir == "." || (p_dir == ".." && current_dir.is_empty())) {
return OK;
+ }
String new_dir;
- if (p_dir != "res://" && p_dir.length() > 1 && p_dir.ends_with("/"))
+ if (p_dir != "res://" && p_dir.length() > 1 && p_dir.ends_with("/")) {
p_dir = p_dir.substr(0, p_dir.length() - 1);
+ }
- if (p_dir.begins_with("/"))
+ if (p_dir.begins_with("/")) {
new_dir = p_dir.substr(1, p_dir.length());
- else if (p_dir.begins_with("res://"))
+ } else if (p_dir.begins_with("res://")) {
new_dir = p_dir.substr(6, p_dir.length());
- else if (current_dir.is_empty())
+ } else if (current_dir.is_empty()) {
new_dir = p_dir;
- else
+ } else {
new_dir = current_dir.plus_file(p_dir);
+ }
//test if newdir exists
new_dir = new_dir.simplify_path();
@@ -125,8 +129,9 @@ Error DirAccessJAndroid::change_dir(String p_dir) {
jstring js = env->NewStringUTF(new_dir.utf8().get_data());
int res = env->CallIntMethod(io, _dir_open, js);
env->DeleteLocalRef(js);
- if (res <= 0)
+ if (res <= 0) {
return ERR_INVALID_PARAMETER;
+ }
env->CallVoidMethod(io, _dir_close, res);
@@ -135,20 +140,21 @@ Error DirAccessJAndroid::change_dir(String p_dir) {
return OK;
}
-String DirAccessJAndroid::get_current_dir(bool p_include_drive) {
+String DirAccessJAndroid::get_current_dir(bool p_include_drive) const {
return "res://" + current_dir;
}
bool DirAccessJAndroid::file_exists(String p_file) {
String sd;
- if (current_dir.is_empty())
+ if (current_dir.is_empty()) {
sd = p_file;
- else
+ } else {
sd = current_dir.plus_file(p_file);
+ }
- FileAccessAndroid *f = memnew(FileAccessAndroid);
+ Ref<FileAccessAndroid> f;
+ f.instantiate();
bool exists = f->file_exists(sd);
- memdelete(f);
return exists;
}
@@ -158,27 +164,30 @@ bool DirAccessJAndroid::dir_exists(String p_dir) {
String sd;
- if (current_dir.is_empty())
+ if (current_dir.is_empty()) {
sd = p_dir;
- else {
- if (p_dir.is_relative_path())
+ } else {
+ if (p_dir.is_relative_path()) {
sd = current_dir.plus_file(p_dir);
- else
+ } else {
sd = fix_path(p_dir);
+ }
}
String path = sd.simplify_path();
- if (path.begins_with("/"))
+ if (path.begins_with("/")) {
path = path.substr(1, path.length());
- else if (path.begins_with("res://"))
+ } else if (path.begins_with("res://")) {
path = path.substr(6, path.length());
+ }
jstring js = env->NewStringUTF(path.utf8().get_data());
int res = env->CallIntMethod(io, _dir_open, js);
env->DeleteLocalRef(js);
- if (res <= 0)
+ if (res <= 0) {
return false;
+ }
env->CallVoidMethod(io, _dir_close, res);
@@ -216,12 +225,9 @@ void DirAccessJAndroid::setup(jobject p_io) {
_dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;");
_dir_close = env->GetMethodID(cls, "dir_close", "(I)V");
_dir_is_dir = env->GetMethodID(cls, "dir_is_dir", "(I)Z");
-
- //(*env)->CallVoidMethod(env,obj,aMethodID, myvar);
}
DirAccessJAndroid::DirAccessJAndroid() {
- id = 0;
}
DirAccessJAndroid::~DirAccessJAndroid() {
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index cdf98187ed..0e1b12cb58 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,8 +36,6 @@
#include <stdio.h>
class DirAccessJAndroid : public DirAccess {
- //AAssetDir* aad;
-
static jobject io;
static jclass cls;
@@ -46,12 +44,12 @@ class DirAccessJAndroid : public DirAccess {
static jmethodID _dir_close;
static jmethodID _dir_is_dir;
- int id;
+ int id = 0;
String current_dir;
String current;
- static DirAccess *create_fs();
+ static Ref<DirAccess> create_fs();
public:
virtual Error list_dir_begin(); ///< This starts dir listing
@@ -64,7 +62,7 @@ public:
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
- virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location
virtual bool file_exists(String p_file);
virtual bool dir_exists(String p_dir);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 505e7ac0eb..3be220d110 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,6 +34,7 @@
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
#include "os_android.h"
+#include "tts_android.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
@@ -42,12 +43,11 @@
#endif
DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
- return (DisplayServerAndroid *)DisplayServer::get_singleton();
+ return static_cast<DisplayServerAndroid *>(DisplayServer::get_singleton());
}
bool DisplayServerAndroid::has_feature(Feature p_feature) const {
switch (p_feature) {
- //case FEATURE_CONSOLE_WINDOW:
case FEATURE_CURSOR_SHAPE:
//case FEATURE_CUSTOM_CURSOR_SHAPE:
//case FEATURE_GLOBAL_MENU:
@@ -64,6 +64,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
case FEATURE_ORIENTATION:
case FEATURE_TOUCHSCREEN:
case FEATURE_VIRTUAL_KEYBOARD:
+ case FEATURE_TEXT_TO_SPEECH:
return true;
default:
return false;
@@ -74,9 +75,37 @@ String DisplayServerAndroid::get_name() const {
return "Android";
}
+bool DisplayServerAndroid::tts_is_speaking() const {
+ return TTS_Android::is_speaking();
+}
+
+bool DisplayServerAndroid::tts_is_paused() const {
+ return TTS_Android::is_paused();
+}
+
+Array DisplayServerAndroid::tts_get_voices() const {
+ return TTS_Android::get_voices();
+}
+
+void DisplayServerAndroid::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ TTS_Android::speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
+}
+
+void DisplayServerAndroid::tts_pause() {
+ TTS_Android::pause();
+}
+
+void DisplayServerAndroid::tts_resume() {
+ TTS_Android::resume();
+}
+
+void DisplayServerAndroid::tts_stop() {
+ TTS_Android::stop();
+}
+
void DisplayServerAndroid::clipboard_set(const String &p_text) {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
- ERR_FAIL_COND(!godot_java);
+ ERR_FAIL_NULL(godot_java);
if (godot_java->has_set_clipboard()) {
godot_java->set_clipboard(p_text);
@@ -87,7 +116,7 @@ void DisplayServerAndroid::clipboard_set(const String &p_text) {
String DisplayServerAndroid::clipboard_get() const {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
- ERR_FAIL_COND_V(!godot_java, String());
+ ERR_FAIL_NULL_V(godot_java, String());
if (godot_java->has_get_clipboard()) {
return godot_java->get_clipboard();
@@ -96,9 +125,32 @@ String DisplayServerAndroid::clipboard_get() const {
}
}
+bool DisplayServerAndroid::clipboard_has() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_NULL_V(godot_java, false);
+
+ if (godot_java->has_has_clipboard()) {
+ return godot_java->has_clipboard();
+ } else {
+ return DisplayServer::clipboard_has();
+ }
+}
+
+Array DisplayServerAndroid::get_display_cutouts() const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_NULL_V(godot_io_java, Array());
+ return godot_io_java->get_display_cutouts();
+}
+
+Rect2i DisplayServerAndroid::get_display_safe_area() const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_NULL_V(godot_io_java, Rect2i());
+ return godot_io_java->get_display_safe_area();
+}
+
void DisplayServerAndroid::screen_set_keep_on(bool p_enable) {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
- ERR_FAIL_COND(!godot_java);
+ ERR_FAIL_NULL(godot_java);
godot_java->set_keep_screen_on(p_enable);
keep_screen_on = p_enable;
@@ -110,14 +162,14 @@ bool DisplayServerAndroid::screen_is_kept_on() const {
void DisplayServerAndroid::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND(!godot_io_java);
+ ERR_FAIL_NULL(godot_io_java);
godot_io_java->set_screen_orientation(p_orientation);
}
DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(int p_screen) const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND_V(!godot_io_java, SCREEN_LANDSCAPE);
+ ERR_FAIL_NULL_V(godot_io_java, SCREEN_LANDSCAPE);
const int orientation = godot_io_java->get_screen_orientation();
ERR_FAIL_INDEX_V_MSG(orientation, 7, SCREEN_LANDSCAPE, "Unrecognized screen orientation");
@@ -137,27 +189,41 @@ Size2i DisplayServerAndroid::screen_get_size(int p_screen) const {
}
Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const {
- GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND_V(!godot_io_java, Rect2i());
- int xywh[4];
- godot_io_java->screen_get_usable_rect(xywh);
- return Rect2i(xywh[0], xywh[1], xywh[2], xywh[3]);
+ Size2i display_size = OS_Android::get_singleton()->get_display_size();
+ return Rect2i(0, 0, display_size.width, display_size.height);
}
int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND_V(!godot_io_java, 0);
+ ERR_FAIL_NULL_V(godot_io_java, 0);
return godot_io_java->get_screen_dpi();
}
+float DisplayServerAndroid::screen_get_scale(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_NULL_V(godot_io_java, 1.0f);
+
+ return godot_io_java->get_scaled_density();
+}
+
+float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ if (!godot_io_java) {
+ ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+
+ return godot_io_java->get_screen_refresh_rate(SCREEN_REFRESH_RATE_FALLBACK);
+}
+
bool DisplayServerAndroid::screen_is_touchscreen(int p_screen) const {
return true;
}
void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND(!godot_io_java);
+ ERR_FAIL_NULL(godot_io_java);
if (godot_io_java->has_vk()) {
godot_io_java->show_vk(p_existing_text, p_multiline, p_max_length, p_cursor_start, p_cursor_end);
@@ -168,7 +234,7 @@ void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text,
void DisplayServerAndroid::virtual_keyboard_hide() {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND(!godot_io_java);
+ ERR_FAIL_NULL(godot_io_java);
if (godot_io_java->has_vk()) {
godot_io_java->hide_vk();
@@ -179,7 +245,7 @@ void DisplayServerAndroid::virtual_keyboard_hide() {
int DisplayServerAndroid::virtual_keyboard_get_height() const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_COND_V(!godot_io_java, 0);
+ ERR_FAIL_NULL_V(godot_io_java, 0);
return godot_io_java->get_vk_height();
}
@@ -243,6 +309,24 @@ DisplayServer::WindowID DisplayServerAndroid::get_window_at_screen_position(cons
return MAIN_WINDOW_ID;
}
+int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return reinterpret_cast<int64_t>(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
+ }
+ case WINDOW_VIEW: {
+ return 0; // Not supported.
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerAndroid::window_attach_instance_id(ObjectID p_instance, DisplayServer::WindowID p_window) {
window_attached_instance_id = p_instance;
}
@@ -372,9 +456,9 @@ void DisplayServerAndroid::reset_window() {
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
- ERR_FAIL_COND(!native_window);
+ ERR_FAIL_NULL(native_window);
- ERR_FAIL_COND(!context_vulkan);
+ ERR_FAIL_NULL(context_vulkan);
VSyncMode last_vsync_mode = context_vulkan->get_vsync_mode(MAIN_WINDOW_ID);
context_vulkan->window_destroy(MAIN_WINDOW_ID);
@@ -435,7 +519,7 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
if (rendering_driver == "vulkan") {
ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
- ERR_FAIL_COND(!native_window);
+ ERR_FAIL_NULL(native_window);
context_vulkan = memnew(VulkanContextAndroid);
if (context_vulkan->initialize() != OK) {
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 669a1c80e4..65bf2ec1a8 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -70,8 +70,8 @@ class DisplayServerAndroid : public DisplayServer {
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
#if defined(VULKAN_ENABLED)
- VulkanContextAndroid *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
+ VulkanContextAndroid *context_vulkan = nullptr;
+ RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
#endif
ObjectID window_attached_instance_id;
@@ -91,8 +91,21 @@ public:
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
+ virtual bool tts_is_speaking() const override;
+ virtual bool tts_is_paused() const override;
+ virtual Array tts_get_voices() const override;
+
+ virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
+ virtual void tts_pause() override;
+ virtual void tts_resume() override;
+ virtual void tts_stop() override;
+
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
+ virtual bool clipboard_has() const override;
+
+ virtual Array get_display_cutouts() const override;
+ virtual Rect2i get_display_safe_area() const override;
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
@@ -105,6 +118,8 @@ public:
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
@@ -123,6 +138,9 @@ public:
virtual Vector<WindowID> get_window_list() const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
+
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 8df61831c2..560f188b82 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,12 +32,10 @@
#include "export_plugin.h"
-void register_android_exporter() {
- String exe_ext;
- if (OS::get_singleton()->get_name() == "Windows") {
- exe_ext = "*.exe";
- }
+#include "core/os/os.h"
+#include "editor/editor_settings.h"
+void register_android_exporter() {
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", "");
diff --git a/platform/android/export/export.h b/platform/android/export/export.h
index 28e09f41db..82ce40f95d 100644
--- a/platform/android/export/export.h
+++ b/platform/android/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 70b36f2350..c692f10fd1 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,26 @@
#include "export_plugin.h"
+#include "gradle_export_util.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/io/image_loader.h"
+#include "core/io/json.h"
+#include "core/io/marshalls.h"
+#include "core/version.h"
+#include "drivers/png/png_driver_common.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
+#include "editor/editor_settings.h"
+#include "main/splash.gen.h"
+#include "platform/android/logo.gen.h"
+#include "platform/android/run_icon.gen.h"
+
+#include <string.h>
+
static const char *android_perms[] = {
"ACCESS_CHECKIN_PROPERTIES",
"ACCESS_COARSE_LOCATION",
@@ -187,9 +207,9 @@ static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash
static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json";
static const int icon_densities_count = 6;
-static const char *launcher_icon_option = "launcher_icons/main_192x192";
-static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432";
-static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432";
+static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
+static const char *launcher_adaptive_icon_foreground_option = PNAME("launcher_icons/adaptive_foreground_432x432");
+static const char *launcher_adaptive_icon_background_option = PNAME("launcher_icons/adaptive_background_432x432");
static const LauncherIcon launcher_icons[icon_densities_count] = {
{ "res/mipmap-xxxhdpi-v4/icon.png", 192 },
@@ -229,7 +249,7 @@ static const int DEFAULT_TARGET_SDK_VERSION = 30; // Should match the value in '
const String SDK_VERSION_RANGE = vformat("%s,%s,1", DEFAULT_MIN_SDK_VERSION, DEFAULT_TARGET_SDK_VERSION);
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
- EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
+ EditorExportPlatformAndroid *ea = static_cast<EditorExportPlatformAndroid *>(ud);
while (!ea->quit_request.is_set()) {
// Check for plugins updates
@@ -389,7 +409,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
List<String> args;
args.push_back("kill-server");
OS::get_singleton()->execute(adb, args);
- };
+ }
}
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
@@ -416,10 +436,10 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co
bool first = true;
for (int i = 0; i < basename.length(); i++) {
char32_t c = basename[i];
- if (c >= '0' && c <= '9' && first) {
+ if (is_digit(c) && first) {
continue;
}
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
+ if (is_ascii_alphanumeric_char(c)) {
name += String::chr(c);
first = false;
}
@@ -462,19 +482,19 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package,
first = true;
continue;
}
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+ if (!is_ascii_identifier_char(c)) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c));
}
return false;
}
- if (first && (c >= '0' && c <= '9')) {
+ if (first && is_digit(c)) {
if (r_error) {
*r_error = TTR("A digit cannot be the first character in a package segment.");
}
return false;
}
- if (first && c == '_') {
+ if (first && is_underscore(c)) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c));
}
@@ -502,8 +522,8 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package,
bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
/*
- * By not compressing files with little or not benefit in doing so,
- * a performance gain is expected attime. Moreover, if the APK is
+ * By not compressing files with little or no benefit in doing so,
+ * a performance gain is expected at runtime. Moreover, if the APK is
* zip-aligned, assets stored as they are can be efficiently read by
* Android by memory-mapping them.
*/
@@ -523,7 +543,7 @@ bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, c
".webp", // Same reasoning as .png
".cfb", // Don't let small config files slow-down startup
".scn", // Binary scenes are usually already compressed
- ".stex", // Streamable textures are usually already compressed
+ ".ctex", // Streamable textures are usually already compressed
// Trailer for easier processing
nullptr
};
@@ -576,8 +596,8 @@ Vector<String> EditorExportPlatformAndroid::get_abis() {
/// List the gdap files in the directory specified by the p_path parameter.
Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) {
Vector<String> dir_files;
- DirAccessRef da = DirAccess::open(p_path);
- if (da) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ if (da.is_valid()) {
da->list_dir_begin();
while (true) {
String file = da->get_next();
@@ -665,7 +685,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
ERR_PRINT(err);
return FAILED;
}
- APKExportData *ed = (APKExportData *)p_userdata;
+ APKExportData *ed = static_cast<APKExportData *>(p_userdata);
Vector<String> abis = get_abis();
bool exported = false;
for (int i = 0; i < p_so.tags.size(); ++i) {
@@ -690,7 +710,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
}
Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
- APKExportData *ed = (APKExportData *)p_userdata;
+ APKExportData *ed = static_cast<APKExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", "assets/");
store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
@@ -705,7 +725,7 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared
ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED,
"Android .so file names must start with \"lib\", but got: " + p_so.path);
Vector<String> abis = get_abis();
- CustomExportData *export_data = (CustomExportData *)p_userdata;
+ CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
bool exported = false;
for (int i = 0; i < p_so.tags.size(); ++i) {
int abi_index = abis.find(p_so.tags[i]);
@@ -787,7 +807,6 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
}
manifest_text += _get_xr_features_tag(p_preset);
- manifest_text += _get_instrumentation_tag(p_preset);
manifest_text += _get_application_tag(p_preset, _has_storage_permission(perms));
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
@@ -814,11 +833,9 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
uint32_t ofs = 8;
uint32_t string_count = 0;
- //uint32_t styles_count = 0;
uint32_t string_flags = 0;
uint32_t string_data_offset = 0;
- //uint32_t styles_offset = 0;
uint32_t string_table_begins = 0;
uint32_t string_table_ends = 0;
Vector<uint8_t> stable_extra;
@@ -843,6 +860,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
bool classify_as_game = p_preset->get("package/classify_as_game");
bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall");
bool exclude_from_recents = p_preset->get("package/exclude_from_recents");
+ bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable"));
Vector<String> perms;
// Write permissions into the perms variable.
@@ -858,10 +876,8 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
int iofs = ofs + 8;
string_count = decode_uint32(&p_manifest[iofs]);
- // iofs + 4 is `styles_count`.
string_flags = decode_uint32(&p_manifest[iofs + 8]);
string_data_offset = decode_uint32(&p_manifest[iofs + 12]);
- // iofs + 16 is `styles_offset`.
uint32_t st_offset = iofs + 20;
string_table.resize(string_count);
@@ -948,10 +964,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(retain_data_on_uninstall, &p_manifest.write[iofs + 16]);
}
- if (tname == "instrumentation" && attrname == "targetPackage") {
- string_table.write[attr_value] = get_package_name(package_name);
- }
-
if (tname == "activity" && attrname == "screenOrientation") {
encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]);
}
@@ -960,6 +972,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(exclude_from_recents, &p_manifest.write[iofs + 16]);
}
+ if (tname == "activity" && attrname == "resizeableActivity") {
+ encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]);
+ }
+
if (tname == "supports-screens") {
if (attrname == "smallScreens") {
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
@@ -975,29 +991,22 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
}
}
- if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") {
- // Update the meta-data 'android:name' attribute based on the selected XR mode.
- if (xr_mode_index == XR_MODE_OPENXR) {
- string_table.write[attr_value] = "com.samsung.android.vr.application.mode";
+ // Hand tracking related configurations
+ if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) {
+ if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_metadata_name") {
+ string_table.write[attr_value] = "com.oculus.handtracking.frequency";
}
- }
- if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") {
- // Update the meta-data 'android:value' attribute based on the selected XR mode.
- if (xr_mode_index == XR_MODE_OPENXR) {
- string_table.write[attr_value] = "vr_only";
+ if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_metadata_value") {
+ string_table.write[attr_value] = (hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH");
}
- }
- if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_metadata_name") {
- if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) {
- string_table.write[attr_value] = "com.oculus.handtracking.frequency";
+ if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_name") {
+ string_table.write[attr_value] = "com.oculus.handtracking.version";
}
- }
- if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_metadata_value") {
- if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) {
- string_table.write[attr_value] = (hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH");
+ if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_value") {
+ string_table.write[attr_value] = "V2.0";
}
}
@@ -1381,6 +1390,7 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &
Vector<String> string_table;
String package_name = p_preset->get("package/name");
+ Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized");
for (uint32_t i = 0; i < string_count; i++) {
uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]);
@@ -1395,9 +1405,8 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &
} else {
String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_");
- String prop = "application/config/name_" + lang;
- if (ProjectSettings::get_singleton()->has_setting(prop)) {
- str = ProjectSettings::get_singleton()->get(prop);
+ if (appnames.has(lang)) {
+ str = appnames[lang];
} else {
str = get_project_name(package_name);
}
@@ -1514,7 +1523,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R
}
if (scale_splash) {
- Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
int width, height;
if (screen_size.width > screen_size.height) {
// scale horizontally
@@ -1679,7 +1688,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
for (int i = 0; i < plugins_configs.size(); i++) {
print_verbose("Found Android plugin " + plugins_configs[i].name);
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), plugins_configs[i].name)), false));
}
plugins_changed.clear();
@@ -1689,7 +1698,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
// All Android devices supporting Vulkan run 64-bit Android,
// so there is usually no point in exporting for 32-bit Android.
const bool is_default = abi == "arm64-v8a";
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default));
}
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
@@ -1740,7 +1749,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
const char **perms = android_perms;
while (*perms) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("permissions"), String(*perms).to_lower())), false));
perms++;
}
}
@@ -1837,7 +1846,8 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
{ \
DirAccess::remove_file_or_error(tmp_export_path); \
return m_err; \
- }
+ } \
+ ((void)0)
// Export to temporary APK before sending to device.
Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags);
@@ -1997,7 +2007,7 @@ String EditorExportPlatformAndroid::get_apksigner_path() {
Error errn;
String build_tools_dir = sdk_path.plus_file("build-tools");
- DirAccessRef da = DirAccess::open(build_tools_dir, &errn);
+ Ref<DirAccess> da = DirAccess::open(build_tools_dir, &errn);
if (errn != OK) {
print_error("Unable to open Android 'build-tools' directory.");
return apksigner_path;
@@ -2114,7 +2124,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
} else {
Error errn;
// Check for the platform-tools directory.
- DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
+ Ref<DirAccess> da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
if (errn != OK) {
err += TTR("Invalid Android SDK path in Editor Settings.");
err += TTR("Missing 'platform-tools' directory!");
@@ -2132,7 +2142,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
}
// Check for the build-tools directory.
- DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
+ Ref<DirAccess> build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
if (errn != OK) {
err += TTR("Invalid Android SDK path in Editor Settings.");
err += TTR("Missing 'build-tools' directory!");
@@ -2250,9 +2260,9 @@ String EditorExportPlatformAndroid::get_apk_expansion_fullpath(const Ref<EditorE
return fullpath;
}
-Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
String fullpath = get_apk_expansion_fullpath(p_preset, p_path);
- Error err = save_pack(p_preset, fullpath);
+ Error err = save_pack(p_preset, p_debug, fullpath);
return err;
}
@@ -2411,12 +2421,12 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
void EditorExportPlatformAndroid::_clear_assets_directory() {
- DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Ref<DirAccess> da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
// Clear the APK assets directory
if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) {
print_verbose("Clearing APK assets directory..");
- DirAccessRef da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
da_assets->erase_contents_recursive();
da_res->remove(APK_ASSETS_DIRECTORY);
}
@@ -2424,7 +2434,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() {
// Clear the AAB assets directory
if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) {
print_verbose("Clearing AAB assets directory..");
- DirAccessRef da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
da_assets->erase_contents_recursive();
da_res->remove(AAB_ASSETS_DIRECTORY);
}
@@ -2444,7 +2454,7 @@ void EditorExportPlatformAndroid::_remove_copied_libs() {
ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message());
Vector<String> libs = json.get_data();
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
for (int i = 0; i < libs.size(); i++) {
print_verbose("Removing previously installed library " + libs[i]);
da->remove(libs[i]);
@@ -2533,14 +2543,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
//test that installed build version is alright
{
print_verbose("Checking build version..");
- FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
String version = f->get_line().strip_edges();
print_verbose("- build version: " + version);
- f->close();
if (version != VERSION_FULL_CONFIG) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
return ERR_UNCONFIGURED;
@@ -2570,20 +2579,19 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
CustomExportData user_data;
user_data.assets_directory = assets_directory;
user_data.debug = p_debug;
- err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
+ err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
if (err != OK) {
EditorNode::add_io_error(TTR("Could not export project files to gradle project\n"));
return err;
}
if (user_data.libs.size() > 0) {
- FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE);
+ Ref<FileAccess> fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE);
JSON json;
fa->store_string(json.stringify(user_data.libs, "\t"));
- fa->close();
}
} else {
print_verbose("Saving apk expansion file..");
- err = save_apk_expansion_file(p_preset, p_path);
+ err = save_apk_expansion_file(p_preset, p_debug, p_path);
if (err != OK) {
EditorNode::add_io_error(TTR("Could not write expansion package file!"));
return err;
@@ -2666,6 +2674,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
}
+ if (debug_keystore.is_relative_path()) {
+ debug_keystore = OS::get_singleton()->get_resource_dir().plus_file(debug_keystore).simplify_path();
+ }
+ if (!FileAccess::exists(debug_keystore)) {
+ EditorNode::add_io_error(TTR("Could not find keystore, unable to export."));
+ return ERR_FILE_CANT_OPEN;
+ }
cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file.
cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias.
@@ -2675,6 +2690,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
+ if (release_keystore.is_relative_path()) {
+ release_keystore = OS::get_singleton()->get_resource_dir().plus_file(release_keystore).simplify_path();
+ }
if (!FileAccess::exists(release_keystore)) {
EditorNode::add_io_error(TTR("Could not find keystore, unable to export."));
return ERR_FILE_CANT_OPEN;
@@ -2749,8 +2767,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_FILE_BAD_PATH;
}
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
if (ep.step(TTR("Creating APK..."), 0)) {
return ERR_SKIP;
@@ -2764,9 +2782,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
int ret = unzGoToFirstFile(pkg);
- zlib_filefunc_def io2 = io;
- FileAccess *dst_f = nullptr;
- io2.opaque = &dst_f;
+ Ref<FileAccess> io2_fa;
+ zlib_filefunc_def io2 = zipio_create_io(&io2_fa);
String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
@@ -2774,7 +2791,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
{ \
DirAccess::remove_file_or_error(tmp_unaligned_path); \
return m_err; \
- }
+ } \
+ ((void)0)
zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
@@ -2791,10 +2809,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
unz_file_info info;
char fname[16384];
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
bool skip = false;
- String file = fname;
+ String file = String::utf8(fname);
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
@@ -2899,10 +2920,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
- err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so);
+ err = export_project_files(p_preset, p_debug, ignore_apk_file, &ed, save_apk_so);
} else {
if (apk_expansion) {
- err = save_apk_expansion_file(p_preset, p_path);
+ err = save_apk_expansion_file(p_preset, p_debug, p_path);
if (err != OK) {
EditorNode::add_io_error(TTR("Could not write expansion package file!"));
return err;
@@ -2911,7 +2932,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
- err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
+ err = export_project_files(p_preset, p_debug, save_apk_file, &ed, save_apk_so);
}
}
@@ -2959,9 +2980,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
ret = unzGoToFirstFile(tmp_unaligned);
- io2 = io;
- dst_f = nullptr;
- io2.opaque = &dst_f;
+ io2 = zipio_create_io(&io2_fa);
zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
// Take files from the unaligned APK and write them out to the aligned one
@@ -2975,8 +2994,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
char fname[16384];
char extra[16384];
ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
- String file = fname;
+ String file = String::utf8(fname);
Vector<uint8_t> data;
data.resize(info.compressed_size);
@@ -3038,7 +3060,7 @@ void EditorExportPlatformAndroid::get_platform_features(List<String> *r_features
r_features->push_back("android");
}
-void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {
}
EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index e0ffaa718b..eeb5aae0f1 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,29 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/config/project_settings.h"
-#include "core/io/dir_access.h"
-#include "core/io/file_access.h"
-#include "core/io/image_loader.h"
-#include "core/io/json.h"
-#include "core/io/marshalls.h"
-#include "core/io/zip_io.h"
-#include "core/os/os.h"
-#include "core/templates/safe_refcount.h"
-#include "core/version.h"
-#include "drivers/png/png_driver_common.h"
-#include "editor/editor_export.h"
-#include "editor/editor_log.h"
-#include "editor/editor_node.h"
-#include "editor/editor_settings.h"
-#include "main/splash.gen.h"
-#include "platform/android/logo.gen.h"
-#include "platform/android/run_icon.gen.h"
+#ifndef ANDROID_EXPORT_PLUGIN_H
+#define ANDROID_EXPORT_PLUGIN_H
#include "godot_plugin_config.h"
-#include "gradle_export_util.h"
-#include <string.h>
+#include "core/io/zip_io.h"
+#include "core/os/os.h"
+#include "editor/editor_export.h"
const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
@@ -228,7 +213,7 @@ public:
String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path);
- Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags);
@@ -246,9 +231,11 @@ public:
virtual void get_platform_features(List<String> *r_features) override;
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override;
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override;
EditorExportPlatformAndroid();
~EditorExportPlatformAndroid();
};
+
+#endif // ANDROID_EXPORT_PLUGIN_H
diff --git a/platform/android/export/godot_plugin_config.cpp b/platform/android/export/godot_plugin_config.cpp
index 205cba3350..3daf6e44cd 100644
--- a/platform/android/export/godot_plugin_config.cpp
+++ b/platform/android/export/godot_plugin_config.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,7 +71,6 @@ PluginConfigAndroid PluginConfigAndroid::resolve_prebuilt_plugin(PluginConfigAnd
Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plugins_base_dir) {
Vector<PluginConfigAndroid> prebuilt_plugins;
- // prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir));
return prebuilt_plugins;
}
diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h
index c016634371..5188f615d4 100644
--- a/platform/android/export/godot_plugin_config.h
+++ b/platform/android/export/godot_plugin_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -103,4 +103,4 @@ struct PluginConfigAndroid {
static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs);
};
-#endif // GODOT_PLUGIN_CONFIG_H
+#endif // ANDROID_GODOT_PLUGIN_CONFIG_H
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 27c84baa44..9a470edfdd 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,8 @@
#include "gradle_export_util.h"
+#include "core/config/project_settings.h"
+
int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) {
switch (screen_orientation) {
case DisplayServer::SCREEN_PORTRAIT:
@@ -73,11 +75,10 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or
// Utility method used to create a directory.
Error create_directory(const String &p_dir) {
if (!DirAccess::exists(p_dir)) {
- DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
Error err = filesystem_da->make_dir_recursive(p_dir);
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
- memdelete(filesystem_da);
}
return OK;
}
@@ -90,10 +91,9 @@ Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
if (err != OK) {
return err;
}
- FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
fa->store_buffer(p_data.ptr(), p_data.size());
- memdelete(fa);
return OK;
}
@@ -108,10 +108,9 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
}
return err;
}
- FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
fa->store_string(p_data);
- memdelete(fa);
return OK;
}
@@ -121,7 +120,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// It's functionality mirrors that of the method save_apk_file.
// This method will be called ONLY when custom build is enabled.
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
- CustomExportData *export_data = (CustomExportData *)p_userdata;
+ CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
print_verbose("Saving project files from " + p_path + " into " + dst_path);
Error err = store_file_at_path(dst_path, p_data);
@@ -151,14 +150,15 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
// Searches the Gradle project res/ directory to find all supported locales
- DirAccessRef da = DirAccess::open("res://android/build/res");
- if (!da) {
+ Ref<DirAccess> da = DirAccess::open("res://android/build/res");
+ if (da.is_null()) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to open Android resources directory.");
}
return ERR_CANT_OPEN;
}
da->list_dir_begin();
+ Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized");
while (true) {
String file = da->get_next();
if (file.is_empty()) {
@@ -169,10 +169,9 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
continue;
}
String locale = file.replace("values-", "").replace("-r", "_");
- String property_name = "application/config/name_" + locale;
String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
- if (ProjectSettings::get_singleton()->has_setting(property_name)) {
- String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
+ if (appnames.has(locale)) {
+ String locale_project_name = appnames[locale];
String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name));
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
store_string_at_path(locale_directory, processed_xml_string);
@@ -233,30 +232,19 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_xr_features;
}
-String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) {
- String package_name = p_preset->get("package/unique_name");
- String manifest_instrumentation_text = vformat(
- " <instrumentation\n"
- " tools:node=\"replace\"\n"
- " android:name=\".GodotInstrumentation\"\n"
- " android:icon=\"@mipmap/icon\"\n"
- " android:label=\"@string/godot_project_name_string\"\n"
- " android:targetPackage=\"%s\" />\n",
- package_name);
- return manifest_instrumentation_text;
-}
-
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
- "tools:replace=\"android:screenOrientation,android:excludeFromRecents\" "
+ "tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
"android:excludeFromRecents=\"%s\" "
- "android:screenOrientation=\"%s\">\n",
+ "android:screenOrientation=\"%s\" "
+ "android:resizeableActivity=\"%s\">\n",
bool_to_string(p_preset->get("package/exclude_from_recents")),
- orientation);
+ orientation,
+ bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
if (uses_xr) {
manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n";
} else {
@@ -278,7 +266,7 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
" android:requestLegacyExternalStorage=\"%s\"\n"
" tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
" tools:ignore=\"GoogleAppIndexingWarning\">\n\n"
- " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n"
+ " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_version_name\" />\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n",
bool_to_string(p_preset->get("user_data_backup/allow")),
bool_to_string(p_preset->get("package/classify_as_game")),
@@ -286,8 +274,6 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
bool_to_string(p_has_storage_permission));
if (uses_xr) {
- manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n";
-
bool hand_tracking_enabled = (int)(p_preset->get("xr_features/hand_tracking")) > XR_HAND_TRACKING_NONE;
if (hand_tracking_enabled) {
int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency");
@@ -295,7 +281,10 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
manifest_application_text += vformat(
" <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.frequency\" android:value=\"%s\" />\n",
hand_tracking_frequency);
+ manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.version\" android:value=\"V2.0\" />\n";
}
+ } else {
+ manifest_application_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.supportedDevices\" />\n";
}
manifest_application_text += _get_activity_tag(p_preset);
manifest_application_text += " </application>\n";
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 3c440f3e29..109852bdfc 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -102,10 +102,8 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset);
-String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset);
-
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset);
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission);
-#endif //GODOT_GRADLE_EXPORT_UTIL_H
+#endif // GODOT_GRADLE_EXPORT_UTIL_H
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 90370878b7..4bb8a13bb6 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,52 +29,53 @@
/*************************************************************************/
#include "file_access_android.h"
+
#include "core/string/print_string.h"
AAssetManager *FileAccessAndroid::asset_manager = nullptr;
-/*void FileAccessAndroid::make_default() {
- create_func=create_android;
-}*/
-
-FileAccess *FileAccessAndroid::create_android() {
+Ref<FileAccess> FileAccessAndroid::create_android() {
return memnew(FileAccessAndroid);
}
Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
+ _close();
+
String path = fix_path(p_path).simplify_path();
- if (path.begins_with("/"))
+ if (path.begins_with("/")) {
path = path.substr(1, path.length());
- else if (path.begins_with("res://"))
+ } else if (path.begins_with("res://")) {
path = path.substr(6, path.length());
+ }
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, ERR_UNAVAILABLE); //can't write on android..
- a = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
- if (!a)
+ asset = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
+ if (!asset) {
return ERR_CANT_OPEN;
- //ERR_FAIL_COND_V(!a,ERR_FILE_NOT_FOUND);
- len = AAsset_getLength(a);
+ }
+ len = AAsset_getLength(asset);
pos = 0;
eof = false;
return OK;
}
-void FileAccessAndroid::close() {
- if (!a)
+void FileAccessAndroid::_close() {
+ if (!asset) {
return;
- AAsset_close(a);
- a = nullptr;
+ }
+ AAsset_close(asset);
+ asset = nullptr;
}
bool FileAccessAndroid::is_open() const {
- return a != nullptr;
+ return asset != nullptr;
}
void FileAccessAndroid::seek(uint64_t p_position) {
- ERR_FAIL_COND(!a);
+ ERR_FAIL_NULL(asset);
- AAsset_seek(a, p_position, SEEK_SET);
+ AAsset_seek(asset, p_position, SEEK_SET);
pos = p_position;
if (pos > len) {
pos = len;
@@ -85,8 +86,8 @@ void FileAccessAndroid::seek(uint64_t p_position) {
}
void FileAccessAndroid::seek_end(int64_t p_position) {
- ERR_FAIL_COND(!a);
- AAsset_seek(a, p_position, SEEK_END);
+ ERR_FAIL_NULL(asset);
+ AAsset_seek(asset, p_position, SEEK_END);
pos = len + p_position;
}
@@ -109,7 +110,7 @@ uint8_t FileAccessAndroid::get_8() const {
}
uint8_t byte;
- AAsset_read(a, &byte, 1);
+ AAsset_read(asset, &byte, 1);
pos++;
return byte;
}
@@ -117,7 +118,7 @@ uint8_t FileAccessAndroid::get_8() const {
uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
- int r = AAsset_read(a, p_dst, p_length);
+ int r = AAsset_read(asset, p_dst, p_length);
if (pos + p_length > len) {
eof = true;
@@ -146,20 +147,22 @@ void FileAccessAndroid::store_8(uint8_t p_dest) {
bool FileAccessAndroid::file_exists(const String &p_path) {
String path = fix_path(p_path).simplify_path();
- if (path.begins_with("/"))
+ if (path.begins_with("/")) {
path = path.substr(1, path.length());
- else if (path.begins_with("res://"))
+ } else if (path.begins_with("res://")) {
path = path.substr(6, path.length());
+ }
AAsset *at = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
- if (!at)
+ if (!at) {
return false;
+ }
AAsset_close(at);
return true;
}
FileAccessAndroid::~FileAccessAndroid() {
- close();
+ _close();
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index bb4ce36947..c16f74ac43 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,45 +35,43 @@
#include <android/asset_manager.h>
#include <android/log.h>
#include <stdio.h>
-//#include <android_native_app_glue.h>
class FileAccessAndroid : public FileAccess {
- static FileAccess *create_android();
- mutable AAsset *a = nullptr;
+ static Ref<FileAccess> create_android();
+ mutable AAsset *asset = nullptr;
mutable uint64_t len = 0;
mutable uint64_t pos = 0;
mutable bool eof = false;
+ void _close();
+
public:
static AAssetManager *asset_manager;
- virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
- virtual void close(); ///< close a file
- virtual bool is_open() const; ///< true when file is open
+ virtual Error _open(const String &p_path, int p_mode_flags); // open a file
+ virtual bool is_open() const; // true when file is open
- virtual void seek(uint64_t p_position); ///< seek to a given position
- virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
- virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_length() const; ///< get size of the file
+ virtual void seek(uint64_t p_position); // seek to a given position
+ virtual void seek_end(int64_t p_position = 0); // seek from the end of file
+ virtual uint64_t get_position() const; // get position in the file
+ virtual uint64_t get_length() const; // get size of the file
- virtual bool eof_reached() const; ///< reading passed EOF
+ virtual bool eof_reached() const; // reading passed EOF
- virtual uint8_t get_8() const; ///< get a byte
+ virtual uint8_t get_8() const; // get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
- virtual Error get_error() const; ///< get last error
+ virtual Error get_error() const; // get last error
virtual void flush();
- virtual void store_8(uint8_t p_dest); ///< store a byte
+ virtual void store_8(uint8_t p_dest); // store a byte
- virtual bool file_exists(const String &p_path); ///< return true if a file exists
+ virtual bool file_exists(const String &p_path); // return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) { return 0; }
virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; }
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; }
- //static void make_default();
-
~FileAccessAndroid();
};
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 3924aacccd..c98e8f1d55 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -33,11 +33,6 @@
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
- <!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
- <meta-data
- android:name="xr_mode_metadata_name"
- android:value="xr_mode_metadata_value" />
-
<!-- XR hand tracking metadata -->
<!-- This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
<!-- Removed at export time if the xr mode is not VR or hand tracking is disabled. -->
@@ -45,6 +40,19 @@
android:name="xr_hand_tracking_metadata_name"
android:value="xr_hand_tracking_metadata_value"/>
+ <!-- XR hand tracking version -->
+ <!-- This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
+ <!-- Removed at export time if the xr mode is not VR or hand tracking is disabled. -->
+ <meta-data
+ android:name="xr_hand_tracking_version_name"
+ android:value="xr_hand_tracking_version_value"/>
+
+ <!-- Supported Meta devices -->
+ <!-- This is removed by the exporter if the xr mode is not VR. -->
+ <meta-data
+ android:name="com.oculus.supportedDevices"
+ android:value="all" />
+
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 5d1a9d7b99..63b10e62b1 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -1,6 +1,4 @@
// Gradle build config for Godot Engine's Android port.
-apply from: 'config.gradle'
-
buildscript {
apply from: 'config.gradle'
@@ -14,7 +12,12 @@ buildscript {
}
}
-apply plugin: 'com.android.application'
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+apply from: 'config.gradle'
allprojects {
repositories {
@@ -33,6 +36,11 @@ allprojects {
}
}
+configurations {
+ // Initializes a placeholder for the devImplementation dependency configuration.
+ devImplementation {}
+}
+
dependencies {
implementation libraries.kotlinStdLib
implementation libraries.androidxFragment
@@ -45,6 +53,7 @@ dependencies {
// Custom build mode. In this scenario this project is the only one around and the Godot
// library is available through the pre-generated godot-lib.*.aar android archive files.
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
+ devImplementation fileTree(dir: 'libs/dev', include: ['*.jar', '*.aar'])
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
}
@@ -66,12 +75,17 @@ dependencies {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
compileOptions {
sourceCompatibility versions.javaVersion
targetCompatibility versions.javaVersion
}
+ kotlinOptions {
+ jvmTarget = versions.javaVersion
+ }
+
assetPacks = [":assetPacks:installTime"]
defaultConfig {
@@ -93,6 +107,8 @@ android {
versionName getExportVersionName()
minSdkVersion getExportMinSdkVersion()
targetSdkVersion getExportTargetSdkVersion()
+
+ missingDimensionStrategy 'products', 'template'
}
lintOptions {
@@ -146,6 +162,18 @@ android {
}
}
+ dev {
+ initWith debug
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.debug
+ } else {
+ signingConfig null
+ }
+ }
+
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
// performed for custom builds.
@@ -167,6 +195,7 @@ android {
assets.srcDirs = ['assets']
}
debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
}
@@ -183,6 +212,12 @@ task copyAndRenameDebugApk(type: Copy) {
rename "android_debug.apk", getExportFilename()
}
+task copyAndRenameDevApk(type: Copy) {
+ from "$buildDir/outputs/apk/dev/android_dev.apk"
+ into getExportPath()
+ rename "android_dev.apk", getExportFilename()
+}
+
task copyAndRenameReleaseApk(type: Copy) {
from "$buildDir/outputs/apk/release/android_release.apk"
into getExportPath()
@@ -195,6 +230,12 @@ task copyAndRenameDebugAab(type: Copy) {
rename "build-debug.aab", getExportFilename()
}
+task copyAndRenameDevAab(type: Copy) {
+ from "$buildDir/outputs/bundle/dev/build-dev.aab"
+ into getExportPath()
+ rename "build-dev.aab", getExportFilename()
+}
+
task copyAndRenameReleaseAab(type: Copy) {
from "$buildDir/outputs/bundle/release/build-release.aab"
into getExportPath()
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 32e03998da..73a412a2b0 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,11 +1,12 @@
ext.versions = [
androidGradlePlugin: '7.0.3',
- compileSdk : 30,
+ compileSdk : 31,
minSdk : 19, // Also update 'platform/android/java/lib/AndroidManifest.xml#minSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
targetSdk : 30, // Also update 'platform/android/java/lib/AndroidManifest.xml#targetSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
buildTools : '30.0.3',
- kotlinVersion : '1.5.10',
+ kotlinVersion : '1.6.21',
fragmentVersion : '1.3.6',
+ nexusPublishVersion: '1.1.0',
javaVersion : 11,
ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
@@ -14,7 +15,7 @@ ext.versions = [
ext.libraries = [
androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion",
- kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion",
+ kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlinVersion",
androidxFragment : "androidx.fragment:fragment:$versions.fragmentVersion",
]
@@ -76,7 +77,7 @@ ext.getGodotEditorVersion = { ->
String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
if (editorVersion == null || editorVersion.isEmpty()) {
// Try the library version first
- editorVersion = getGodotLibraryVersion()
+ editorVersion = getGodotLibraryVersionName()
if (editorVersion.isEmpty()) {
// Fallback value.
@@ -86,13 +87,27 @@ ext.getGodotEditorVersion = { ->
return editorVersion
}
-ext.getGodotLibraryVersion = { ->
+ext.getGodotLibraryVersionCode = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionCode
+}
+
+ext.getGodotLibraryVersionName = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionName
+}
+
+ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
// Attempt to read the version from the `version.py` file.
- String libraryVersion = ""
+ String libraryVersionName = ""
+ int libraryVersionCode = 0
File versionFile = new File("../../../version.py")
if (versionFile.isFile()) {
- List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
def map = [:]
List<String> lines = versionFile.readLines()
@@ -110,15 +125,48 @@ ext.getGodotLibraryVersion = { ->
}
if (requiredKeys.empty) {
- libraryVersion = map.values().join(".")
+ libraryVersionName = map.values().join(".")
+ try {
+ if (map.containsKey("patch")) {
+ libraryVersionCode = Integer.parseInt(map["patch"])
+ }
+
+ if (map.containsKey("minor")) {
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ }
+
+ if (map.containsKey("major")) {
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ }
+ } catch (NumberFormatException ignore) {
+ libraryVersionCode = 1
+ }
}
}
- if (libraryVersion.isEmpty()) {
+ if (libraryVersionName.isEmpty()) {
// Fallback value in case we're unable to read the file.
- libraryVersion = "custom_build"
+ libraryVersionName = "custom_build"
+ }
+
+ if (libraryVersionCode == 0) {
+ libraryVersionCode = 1
}
- return libraryVersion
+
+ return [libraryVersionName, libraryVersionCode]
+}
+
+ext.getGodotLibraryVersion = { ->
+ List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
+ return generateGodotLibraryVersion(requiredKeys)
+}
+
+ext.getGodotPublishVersion = { ->
+ List<String> requiredKeys = ["major", "minor", "patch", "status"]
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = generateGodotLibraryVersion(requiredKeys)
+ return versionName
}
final String VALUE_SEPARATOR_REGEX = "\\|"
diff --git a/platform/android/java/app/res/values/themes.xml b/platform/android/java/app/res/values/themes.xml
index 99f723f5ba..d64b50ca45 100644
--- a/platform/android/java/app/res/values/themes.xml
+++ b/platform/android/java/app/res/values/themes.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"/>
+ <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar"/>
- <style name="GodotAppSplashTheme" parent="@style/GodotAppMainTheme">
+ <style name="GodotAppSplashTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<item name="android:windowBackground">@drawable/splash_drawable</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle
index e38d7b2ba6..ba53aefe7f 100644
--- a/platform/android/java/app/settings.gradle
+++ b/platform/android/java/app/settings.gradle
@@ -1,2 +1,15 @@
// This is the root directory of the Godot custom build.
+pluginManagement {
+ apply from: 'config.gradle'
+
+ plugins {
+ id 'com.android.application' version versions.androidGradlePlugin
+ id 'org.jetbrains.kotlin.android' version versions.kotlinVersion
+ }
+ repositories {
+ gradlePluginPortal()
+ google()
+ }
+}
+
include ':assetPacks:installTime'
diff --git a/platform/android/java/app/src/com/godot/game/GodotApp.java b/platform/android/java/app/src/com/godot/game/GodotApp.java
index 955446b0c2..c9684bce14 100644
--- a/platform/android/java/app/src/com/godot/game/GodotApp.java
+++ b/platform/android/java/app/src/com/godot/game/GodotApp.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index efdcc6c77b..da30bd3a95 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -1,18 +1,25 @@
-apply from: 'app/config.gradle'
-
buildscript {
apply from: 'app/config.gradle'
repositories {
google()
mavenCentral()
+ maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath libraries.androidGradlePlugin
classpath libraries.kotlinGradlePlugin
+ classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
}
}
+plugins {
+ id 'io.github.gradle-nexus.publish-plugin'
+}
+
+apply from: 'app/config.gradle'
+apply from: 'scripts/publish-root.gradle'
+
allprojects {
repositories {
google()
@@ -22,21 +29,22 @@ allprojects {
ext {
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
- supportedTargets = ["release", "debug"]
+ supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"]
+ supportedFlavors = ["editor", "template"]
- // Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
- // This command is usually used by Android Studio.
+ // Used by gradle to specify which architecture to build for by default when running
+ // `./gradlew build` (this command is usually used by Android Studio).
// If building manually on the command line, it's recommended to use the
- // `./gradlew generateGodotTemplates` build command instead after running the `scons` command.
- // The defaultAbi must be one of the {supportedAbis} values.
- defaultAbi = "arm64v8"
+ // `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
+ // The {selectedAbis} values must be from the {supportedAbis} values.
+ selectedAbis = ["arm64v8"]
}
def rootDir = "../../.."
def binDir = "$rootDir/bin/"
-def getSconsTaskName(String buildType) {
- return "compileGodotNativeLibs" + buildType.capitalize()
+def getSconsTaskName(String flavor, String buildType, String abi) {
+ return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}
/**
@@ -51,6 +59,17 @@ task copyDebugBinaryToBin(type: Copy) {
}
/**
+ * Copy the generated 'android_dev.apk' binary template into the Godot bin directory.
+ * Depends on the app build task to ensure the binary is generated prior to copying.
+ */
+task copyDevBinaryToBin(type: Copy) {
+ dependsOn ':app:assembleDev'
+ from('app/build/outputs/apk/dev')
+ into(binDir)
+ include('android_dev.apk')
+}
+
+/**
* Copy the generated 'android_release.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
@@ -66,7 +85,7 @@ task copyReleaseBinaryToBin(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
include('godot-lib.debug.aar')
@@ -77,18 +96,40 @@ task copyDebugAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToBin(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.debug.aar')
}
/**
+ * Copy the Godot android library archive dev file into the app module dev libs directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToAppModule(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into('app/libs/dev')
+ include('godot-lib.dev.aar')
+}
+
+/**
+ * Copy the Godot android library archive dev file into the root bin directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToBin(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into(binDir)
+ include('godot-lib.dev.aar')
+}
+
+/**
* Copy the Godot android library archive release file into the app module release libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into('app/libs/release')
include('godot-lib.release.aar')
@@ -99,7 +140,7 @@ task copyReleaseAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToBin(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.release.aar')
@@ -107,7 +148,7 @@ task copyReleaseAARToBin(type: Copy) {
/**
* Generate Godot custom build template by zipping the source files from the app directory, as well
- * as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'.
+ * as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
* The zip file also includes some gradle tools to allow building of the custom build.
*/
task zipCustomBuild(type: Zip) {
@@ -124,8 +165,21 @@ task zipCustomBuild(type: Zip) {
def templateExcludedBuildTask() {
// We exclude these gradle tasks so we can run the scons command manually.
def excludedTasks = []
- for (String buildType : supportedTargets) {
- excludedTasks += ":lib:" + getSconsTaskName(buildType)
+ if (!isAndroidStudio()) {
+ logger.lifecycle("Excluding Android studio build tasks")
+ for (String flavor : supportedFlavors) {
+ for (String buildType : supportedTargetsMap.keySet()) {
+ if (buildType == "release" && flavor == "editor") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+
+ for (String abi : selectedAbis) {
+ excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
+ }
+ }
+ }
}
return excludedTasks
}
@@ -134,7 +188,7 @@ def templateBuildTasks() {
def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
- for (String target : supportedTargets) {
+ for (String target : supportedTargetsMap.keySet()) {
File targetLibs = new File("lib/libs/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
@@ -155,6 +209,55 @@ def templateBuildTasks() {
return tasks
}
+def isAndroidStudio() {
+ def sysProps = System.getProperties()
+ return sysProps != null && sysProps['idea.platform.prefix'] != null
+}
+
+task copyEditorDebugBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDebug'
+ from('editor/build/outputs/apk/debug')
+ into(binDir)
+ include('android_editor.apk')
+}
+
+task copyEditorDevBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDev'
+ from('editor/build/outputs/apk/dev')
+ into(binDir)
+ include('android_editor_dev.apk')
+}
+
+/**
+ * Generate the Godot Editor Android apk.
+ *
+ * Note: The Godot 'tools' shared libraries must have been generated (via scons) prior to running
+ * this gradle task. The task will only build the apk(s) for which the shared libraries is
+ * available.
+ */
+task generateGodotEditor {
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+
+ def tasks = []
+
+ for (String target : supportedTargetsMap.keySet()) {
+ if (target == "release") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+ File targetLibs = new File("lib/libs/tools/" + target)
+ if (targetLibs != null
+ && targetLibs.isDirectory()
+ && targetLibs.listFiles() != null
+ && targetLibs.listFiles().length > 0) {
+ tasks += "copyEditor${target.capitalize()}BinaryToBin"
+ }
+ }
+
+ dependsOn = tasks
+}
+
/**
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
*/
@@ -179,7 +282,27 @@ task generateDevTemplate {
}
/**
- * Clean the generated artifacts.
+ * Clean the generated editor artifacts.
+ */
+task cleanGodotEditor(type: Delete) {
+ // Delete the generated native tools libs
+ delete("lib/libs/tools")
+
+ // Delete the library generated AAR files
+ delete("lib/build/outputs/aar")
+
+ // Delete the generated binary apks
+ delete("editor/build/outputs/apk")
+
+ // Delete the Godot editor apks in the Godot bin directory
+ delete("$binDir/android_editor.apk")
+ delete("$binDir/android_editor_dev.apk")
+
+ finalizedBy getTasksByName("clean", true)
+}
+
+/**
+ * Clean the generated template artifacts.
*/
task cleanGodotTemplates(type: Delete) {
// Delete the generated native libs
@@ -196,9 +319,11 @@ task cleanGodotTemplates(type: Delete) {
// Delete the Godot templates in the Godot bin directory
delete("$binDir/android_debug.apk")
+ delete("$binDir/android_dev.apk")
delete("$binDir/android_release.apk")
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.debug.aar")
+ delete("$binDir/godot-lib.dev.aar")
delete("$binDir/godot-lib.release.aar")
finalizedBy getTasksByName("clean", true)
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
new file mode 100644
index 0000000000..dd167c3880
--- /dev/null
+++ b/platform/android/java/editor/build.gradle
@@ -0,0 +1,83 @@
+// Gradle build config for Godot Engine's Android port.
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+dependencies {
+ implementation libraries.kotlinStdLib
+ implementation libraries.androidxFragment
+ implementation project(":lib")
+
+ implementation "androidx.window:window:1.0.0"
+}
+
+android {
+ compileSdkVersion versions.compileSdk
+ buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
+
+ 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()
+ minSdkVersion versions.minSdk
+ //noinspection ExpiredTargetSdkVersion - Restrict to version 29 until https://github.com/godotengine/godot/pull/51815 is submitted
+ targetSdkVersion 29 // versions.targetSdk
+
+ missingDimensionStrategy 'products', 'editor'
+ }
+
+ compileOptions {
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
+ }
+
+ kotlinOptions {
+ jvmTarget = versions.javaVersion
+ }
+
+ buildTypes {
+ dev {
+ initWith debug
+ applicationIdSuffix ".dev"
+ }
+
+ debug {
+ initWith release
+
+ // Need to swap with the release signing config when this is ready for public release.
+ signingConfig signingConfigs.debug
+ }
+
+ release {
+ // This buildtype is disabled below.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ }
+ }
+
+ packagingOptions {
+ // 'doNotStrip' is enabled for development within Android Studio
+ if (shouldNotStrip()) {
+ doNotStrip '**/*.so'
+ }
+ }
+
+ // Disable 'release' buildtype.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.buildType.name == "release") {
+ setIgnore(true)
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all { output ->
+ def suffix = variant.name == "dev" ? "_dev" : ""
+ output.outputFileName = "android_editor${suffix}.apk"
+ }
+ }
+}
diff --git a/platform/android/java/editor/src/dev/res/values/strings.xml b/platform/android/java/editor/src/dev/res/values/strings.xml
new file mode 100644
index 0000000000..45fae3fd39
--- /dev/null
+++ b/platform/android/java/editor/src/dev/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x (dev)</string>
+</resources>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..659caf7ab4
--- /dev/null
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.godotengine.editor"
+ android:installLocation="auto">
+
+ <supports-screens
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true"
+ android:xlargeScreens="true" />
+
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowBackup="false"
+ android:icon="@mipmap/icon"
+ android:label="@string/godot_editor_name_string"
+ tools:ignore="GoogleAppIndexingWarning"
+ android:requestLegacyExternalStorage="true">
+
+ <activity
+ android:name=".GodotProjectManager"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:exported="true"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:process=":GodotProjectManager">
+
+ <layout android:defaultHeight="@dimen/editor_default_window_height"
+ android:defaultWidth="@dimen/editor_default_window_width" />
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".GodotEditor"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:process=":GodotEditor"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ <layout android:defaultHeight="@dimen/editor_default_window_height"
+ android:defaultWidth="@dimen/editor_default_window_width" />
+ </activity>
+
+ <activity
+ android:name=".GodotGame"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:label="@string/godot_project_name_string"
+ android:process=":GodotGame"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ <layout android:defaultHeight="@dimen/editor_default_window_height"
+ android:defaultWidth="@dimen/editor_default_window_width" />
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
new file mode 100644
index 0000000000..c05ed965ca
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* GodotEditor.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.editor;
+
+import org.godotengine.godot.FullScreenGodotApp;
+import org.godotengine.godot.utils.PermissionsUtil;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+
+import androidx.annotation.Nullable;
+import androidx.window.layout.WindowMetrics;
+import androidx.window.layout.WindowMetricsCalculator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Base class for the Godot Android Editor activities.
+ *
+ * This provides the basic templates for the activities making up this application.
+ * Each derived activity runs in its own process, which enable up to have several instances of
+ * the Godot engine up and running at the same time.
+ *
+ * It also plays the role of the primary editor window.
+ */
+public class GodotEditor extends FullScreenGodotApp {
+ private static final boolean WAIT_FOR_DEBUGGER = false;
+ private static final String COMMAND_LINE_PARAMS = "command_line_params";
+
+ private static final String EDITOR_ARG = "--editor";
+ private static final String PROJECT_MANAGER_ARG = "--project-manager";
+
+ private final List<String> commandLineParams = new ArrayList<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ PermissionsUtil.requestManifestPermissions(this);
+
+ String[] params = getIntent().getStringArrayExtra(COMMAND_LINE_PARAMS);
+ updateCommandLineParams(params);
+
+ if (BuildConfig.BUILD_TYPE.equals("debug") && WAIT_FOR_DEBUGGER) {
+ Debug.waitForDebugger();
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ private void updateCommandLineParams(@Nullable String[] args) {
+ // Update the list of command line params with the new args
+ commandLineParams.clear();
+ if (args != null && args.length > 0) {
+ commandLineParams.addAll(Arrays.asList(args));
+ }
+ }
+
+ @Override
+ public List<String> getCommandLine() {
+ return commandLineParams;
+ }
+
+ @Override
+ public void onNewGodotInstanceRequested(String[] args) {
+ // Parse the arguments to figure out which activity to start.
+ Class<?> targetClass = GodotGame.class;
+ // Whether we should launch the new godot instance in an adjacent window
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
+ boolean launchAdjacent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && (isInMultiWindowMode() || isLargeScreen());
+
+ for (String arg : args) {
+ if (EDITOR_ARG.equals(arg)) {
+ targetClass = GodotEditor.class;
+ launchAdjacent = false;
+ break;
+ }
+
+ if (PROJECT_MANAGER_ARG.equals(arg)) {
+ targetClass = GodotProjectManager.class;
+ launchAdjacent = false;
+ break;
+ }
+ }
+
+ // Launch a new activity
+ Intent newInstance = new Intent(this, targetClass)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(COMMAND_LINE_PARAMS, args);
+ if (launchAdjacent) {
+ newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
+ }
+ startActivity(newInstance);
+ }
+
+ protected boolean isLargeScreen() {
+ WindowMetrics metrics =
+ WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
+
+ // Get the screen's density scale
+ float scale = getResources().getDisplayMetrics().density;
+
+ // Get the minimum window size
+ float minSize = Math.min(metrics.getBounds().width(), metrics.getBounds().height());
+ float minSizeDp = minSize / scale;
+ return minSizeDp >= 840f; // Correspond to the EXPANDED window size class.
+ }
+
+ @Override
+ public void setRequestedOrientation(int requestedOrientation) {
+ if (!overrideOrientationRequest()) {
+ super.setRequestedOrientation(requestedOrientation);
+ }
+ }
+
+ /**
+ * The Godot Android Editor sets its own orientation via its AndroidManifest
+ */
+ protected boolean overrideOrientationRequest() {
+ return true;
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
index 7f5fd8627c..12766775a8 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* GodotInstrumentation.java */
+/* GodotGame.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,23 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot;
+package org.godotengine.editor;
-import android.app.Instrumentation;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class GodotInstrumentation extends Instrumentation {
- private Intent intent;
-
- @Override
- public void onCreate(Bundle arguments) {
- intent = arguments.getParcelable("intent");
- start();
- }
-
- @Override
- public void onStart() {
- startActivitySync(intent);
+/**
+ * Drives the 'run project' window of the Godot Editor.
+ */
+public class GodotGame extends GodotEditor {
+ protected boolean overrideOrientationRequest() {
+ return false;
}
}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
new file mode 100644
index 0000000000..d30f66bb8c
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* GodotProjectManager.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.editor;
+
+/**
+ * Launcher activity for the Godot Android Editor.
+ *
+ * It presents the user with the project manager interface.
+ * Upon selection of a project, this activity (via its parent logic) starts the
+ * {@link GodotEditor} activity.
+ */
+public class GodotProjectManager extends GodotEditor {
+}
diff --git a/platform/android/java/editor/src/main/res/values/dimens.xml b/platform/android/java/editor/src/main/res/values/dimens.xml
new file mode 100644
index 0000000000..03fb6184d2
--- /dev/null
+++ b/platform/android/java/editor/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="editor_default_window_height">600dp</dimen>
+ <dimen name="editor_default_window_width">800dp</dimen>
+</resources>
diff --git a/platform/android/java/editor/src/main/res/values/strings.xml b/platform/android/java/editor/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..e8ce34f34d
--- /dev/null
+++ b/platform/android/java/editor/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x</string>
+</resources>
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index 2de62271c4..90dc61a6ac 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -16,12 +16,13 @@
<service android:name=".GodotDownloaderService" />
- </application>
+ <activity
+ android:name=".utils.ProcessPhoenix"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:process=":phoenix"
+ android:exported="false"
+ />
- <instrumentation
- android:icon="@mipmap/icon"
- android:label="@string/godot_project_name_string"
- android:name="org.godotengine.godot.GodotInstrumentation"
- android:targetPackage="org.godotengine.godot" />
+ </application>
</manifest>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index fbed4ed078..6b82326a27 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -1,5 +1,14 @@
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+ext {
+ PUBLISH_VERSION = getGodotPublishVersion()
+ PUBLISH_ARTIFACT_ID = 'godot'
+}
+
+apply from: "../scripts/publish-module.gradle"
dependencies {
implementation libraries.kotlinStdLib
@@ -11,21 +20,38 @@ def pathToRootDir = "../../../../"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
-
ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
- manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()]
+ manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersionName()]
}
+ namespace = "org.godotengine.godot"
+
compileOptions {
sourceCompatibility versions.javaVersion
targetCompatibility versions.javaVersion
}
+ kotlinOptions {
+ jvmTarget = versions.javaVersion
+ }
+
+ buildTypes {
+ dev {
+ initWith debug
+ }
+ }
+
+ flavorDimensions "products"
+ productFlavors {
+ editor {}
+ template {}
+ }
+
lintOptions {
abortOnError false
disable 'MissingTranslation', 'UnusedResources'
@@ -49,24 +75,50 @@ android {
aidl.srcDirs = ['aidl']
assets.srcDirs = ['assets']
}
+
debug.jniLibs.srcDirs = ['libs/debug']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
+
+ // Editor jni library
+ editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
+ editorDev.jniLibs.srcDirs = ['libs/tools/dev']
+ }
+
+ // Disable 'editorRelease'.
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.name == "editorRelease") {
+ setIgnore(true)
+ }
}
libraryVariants.all { variant ->
- variant.outputs.all { output ->
- output.outputFileName = "godot-lib.${variant.name}.aar"
+ def flavorName = variant.getFlavorName()
+ if (flavorName == null || flavorName == "") {
+ throw new GradleException("Invalid product flavor: $flavorName")
}
- def buildType = variant.buildType.name.capitalize()
+ boolean toolsFlag = flavorName == "editor"
+
+ def buildType = variant.buildType.name
+ if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
+ throw new GradleException("Invalid build type: $buildType")
+ }
- def releaseTarget = buildType.toLowerCase()
- if (releaseTarget == null || releaseTarget == "") {
- throw new GradleException("Invalid build type: " + buildType)
+ def sconsTarget = supportedTargetsMap[buildType]
+ if (sconsTarget == null || sconsTarget == "") {
+ throw new GradleException("Invalid scons target: $sconsTarget")
}
- if (!supportedAbis.contains(defaultAbi)) {
- throw new GradleException("Invalid default abi: " + defaultAbi)
+ // Update the name of the generated library
+ def outputSuffix = "${buildType}.aar"
+ if (toolsFlag) {
+ outputSuffix = "tools.$outputSuffix"
+ }
+ variant.outputs.all { output ->
+ output.outputFileName = "godot-lib.${outputSuffix}"
}
// Find scons' executable path
@@ -79,13 +131,11 @@ android {
for (ext in sconsExts) {
String sconsNameExt = sconsName + ext
logger.lifecycle("Checking $sconsNameExt")
-
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
if (sconsExecutableFile != null) {
// We're done!
break
}
-
// Check all the options in path
List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
if (!allOptions.isEmpty()) {
@@ -94,21 +144,34 @@ android {
break
}
}
-
if (sconsExecutableFile == null) {
throw new GradleException("Unable to find executable path for the '$sconsName' command.")
} else {
logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
}
- // Creating gradle task to generate the native libraries for the default abi.
- def taskName = getSconsTaskName(buildType)
- tasks.create(name: taskName, type: Exec) {
- executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors()
- }
+ for (String selectedAbi : selectedAbis) {
+ if (!supportedAbis.contains(selectedAbi)) {
+ throw new GradleException("Invalid selected abi: $selectedAbi")
+ }
- // Schedule the tasks so the generated libs are present before the aar file is packaged.
- tasks["merge${buildType}JniLibFolders"].dependsOn taskName
+ // Creating gradle task to generate the native libraries for the selected abi.
+ def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
+ tasks.create(name: taskName, type: Exec) {
+ executable sconsExecutableFile.absolutePath
+ args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ }
+
+ // Schedule the tasks so the generated libs are present before the aar file is packaged.
+ tasks["merge${flavorName.capitalize()}${buildType.capitalize()}JniLibFolders"].dependsOn taskName
+ }
}
+
+ // TODO: Enable when issues with AGP 7.1+ are resolved (https://github.com/GodotVR/godot_openxr/issues/187).
+// publishing {
+// singleVariant("templateRelease") {
+// withSourcesJar()
+// withJavadocJar()
+// }
+// }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
index b12844702a..afe82cd8f3 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
index 3600706c7c..f21f88db0a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,8 @@
package org.godotengine.godot;
-import android.content.ComponentName;
+import org.godotengine.godot.utils.ProcessPhoenix;
+
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -65,16 +66,13 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
} else {
Log.v(TAG, "Creating new Godot fragment instance.");
godotFragment = initGodotInstance();
- if (godotFragment == null) {
- throw new IllegalStateException("Godot instance must be non-null.");
- }
-
getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();
}
}
@Override
public void onDestroy() {
+ Log.v(TAG, "Destroying Godot app...");
super.onDestroy();
onGodotForceQuit(godotFragment);
}
@@ -82,27 +80,21 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
@Override
public final void onGodotForceQuit(Godot instance) {
if (instance == godotFragment) {
- System.exit(0);
+ Log.v(TAG, "Force quitting Godot instance");
+ ProcessPhoenix.forceQuit(this);
}
}
@Override
public final void onGodotRestartRequested(Godot instance) {
if (instance == godotFragment) {
- // HACK:
- //
- // Currently it's very hard to properly deinitialize Godot on Android to restart the game
+ // It's very hard to properly de-initialize Godot on Android to restart the game
// from scratch. Therefore, we need to kill the whole app process and relaunch it.
//
// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
// releasing and reloading native libs or resetting their state somehow and clearing statics).
- //
- // Using instrumentation is a way of making the whole app process restart, because Android
- // will kill any process of the same package which was already running.
- //
- Bundle args = new Bundle();
- args.putParcelable("intent", getIntent());
- startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args);
+ Log.v(TAG, "Restarting Godot instance...");
+ ProcessPhoenix.triggerRebirth(this);
}
}
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 17ff3c75c0..cafae94d62 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,7 @@ import static android.content.Context.WINDOW_SERVICE;
import org.godotengine.godot.input.GodotEditText;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
+import org.godotengine.godot.tts.GodotTTS;
import org.godotengine.godot.utils.GodotNetUtils;
import org.godotengine.godot.utils.PermissionsUtil;
import org.godotengine.godot.xr.XRMode;
@@ -47,7 +48,6 @@ import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -166,6 +166,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public static GodotIO io;
public static GodotNetUtils netUtils;
+ public static GodotTTS tts;
public interface ResultCallback {
void callback(int requestCode, int resultCode, Intent data);
@@ -333,9 +334,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void restart() {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotRestartRequested(this);
+ }
+ });
}
public void alert(final String message, final String title) {
@@ -457,15 +460,12 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
io = new GodotIO(activity);
GodotLib.io = io;
netUtils = new GodotNetUtils(activity);
+ tts = new GodotTTS(activity);
mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
- mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME);
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
- mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
GodotLib.initialize(activity, this, activity.getAssets(), use_apk_expansion);
@@ -508,17 +508,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
use_debug_opengl = true;
} else if (command_line[i].equals("--use_immersive")) {
use_immersive = true;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
- window.getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
- View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-
- UiChangeListener();
- }
+ window.getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
+ View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ UiChangeListener();
} else if (command_line[i].equals("--use_apk_expansion")) {
use_apk_expansion = true;
} else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
@@ -660,15 +657,18 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
- public String getClipboard() {
- String copiedText = "";
-
- if (mClipboard.getPrimaryClip() != null) {
- ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);
- copiedText = item.getText().toString();
- }
+ public boolean hasClipboard() {
+ return mClipboard.hasPrimaryClip();
+ }
- return copiedText;
+ public String getClipboard() {
+ ClipData clipData = mClipboard.getPrimaryClip();
+ if (clipData == null)
+ return "";
+ CharSequence text = clipData.getItemAt(0).getText();
+ if (text == null)
+ return "";
+ return text.toString();
}
public void setClipboard(String p_text) {
@@ -694,7 +694,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
- if (use_immersive && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
+ if (use_immersive) {
Window window = getActivity().getWindow();
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
@@ -714,58 +714,89 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
final View decorView = getActivity().getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- decorView.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
});
}
- @Override
- public void onSensorChanged(SensorEvent event) {
+ public float[] getRotatedValues(float values[]) {
+ if (values == null || values.length != 3) {
+ return values;
+ }
+
Display display =
((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int displayRotation = display.getRotation();
- float[] adjustedValues = new float[3];
- final int[][] axisSwap = {
- { 1, -1, 0, 1 }, // ROTATION_0
- { -1, -1, 1, 0 }, // ROTATION_90
- { -1, 1, 0, 1 }, // ROTATION_180
- { 1, 1, 1, 0 }
- }; // ROTATION_270
+ float[] rotatedValues = new float[3];
+ switch (displayRotation) {
+ case Surface.ROTATION_0:
+ rotatedValues[0] = values[0];
+ rotatedValues[1] = values[1];
+ rotatedValues[2] = values[2];
+ break;
+ case Surface.ROTATION_90:
+ rotatedValues[0] = -values[1];
+ rotatedValues[1] = values[0];
+ rotatedValues[2] = values[2];
+ break;
+ case Surface.ROTATION_180:
+ rotatedValues[0] = -values[0];
+ rotatedValues[1] = -values[1];
+ rotatedValues[2] = values[2];
+ break;
+ case Surface.ROTATION_270:
+ rotatedValues[0] = values[1];
+ rotatedValues[1] = -values[0];
+ rotatedValues[2] = values[2];
+ break;
+ }
- final int[] as = axisSwap[displayRotation];
- adjustedValues[0] = (float)as[0] * event.values[as[2]];
- adjustedValues[1] = (float)as[1] * event.values[as[3]];
- adjustedValues[2] = event.values[2];
+ return rotatedValues;
+ }
- final float x = adjustedValues[0];
- final float y = adjustedValues[1];
- final float z = adjustedValues[2];
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mRenderView == null) {
+ return;
+ }
final int typeOfSensor = event.sensor.getType();
- if (mRenderView != null) {
- mRenderView.queueOnRenderThread(() -> {
- if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) {
- GodotLib.accelerometer(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_GRAVITY) {
- GodotLib.gravity(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) {
- GodotLib.magnetometer(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_GYROSCOPE) {
- GodotLib.gyroscope(x, -y, z);
- }
- });
+ switch (typeOfSensor) {
+ case Sensor.TYPE_ACCELEROMETER: {
+ float[] rotatedValues = getRotatedValues(event.values);
+ mRenderView.queueOnRenderThread(() -> {
+ GodotLib.accelerometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
+ });
+ break;
+ }
+ case Sensor.TYPE_GRAVITY: {
+ float[] rotatedValues = getRotatedValues(event.values);
+ mRenderView.queueOnRenderThread(() -> {
+ GodotLib.gravity(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
+ });
+ break;
+ }
+ case Sensor.TYPE_MAGNETIC_FIELD: {
+ float[] rotatedValues = getRotatedValues(event.values);
+ mRenderView.queueOnRenderThread(() -> {
+ GodotLib.magnetometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
+ });
+ break;
+ }
+ case Sensor.TYPE_GYROSCOPE: {
+ float[] rotatedValues = getRotatedValues(event.values);
+ mRenderView.queueOnRenderThread(() -> {
+ GodotLib.gyroscope(rotatedValues[0], rotatedValues[1], rotatedValues[2]);
+ });
+ break;
+ }
}
}
@@ -822,9 +853,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private void forceQuit() {
// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
// native Godot components that is started in Godot#onVideoInit.
- if (godotHost != null) {
- godotHost.onGodotForceQuit(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotForceQuit(this);
+ }
+ });
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
@@ -848,9 +881,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// Create Hex String
StringBuilder hexString = new StringBuilder();
- for (int i = 0; i < messageDigest.length; i++) {
- String s = Integer.toHexString(0xFF & messageDigest[i]);
-
+ for (byte b : messageDigest) {
+ String s = Integer.toHexString(0xFF & b);
if (s.length() == 1) {
s = "0" + s;
}
@@ -973,6 +1005,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
progress.mOverallTotal));
}
+
public void initInputDevices() {
mRenderView.initInputDevices();
}
@@ -981,4 +1014,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
+
+ @Keep
+ private void createNewGodotInstance(String[] args) {
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onNewGodotInstanceRequested(args);
+ }
+ });
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
index 9784d51182..c6c5b4953d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
index 09337ef989..90a046a7a7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 d5b0b67903..08da1b1832 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,8 @@
/*************************************************************************/
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;
@@ -43,7 +45,6 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
-import android.opengl.GLSurfaceView;
import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 7b22895994..2e7b67194f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -60,8 +60,16 @@ public interface GodotHost {
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host
+ * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
* to perform the appropriate action(s).
*/
default void onGodotRestartRequested(Godot instance) {}
+
+ /**
+ * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to
+ * perform the appropriate action(s).
+ *
+ * @param args Arguments used to initialize the new instance.
+ */
+ default void onNewGodotInstanceRequested(String[] args) {}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index 5f354b6b4c..a8e3669ac6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,6 +38,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.graphics.Point;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -51,6 +52,7 @@ import android.view.DisplayCutout;
import android.view.WindowInsets;
import java.io.IOException;
+import java.util.List;
import java.util.Locale;
// Wrapper for native library
@@ -222,11 +224,41 @@ public class GodotIO {
}
public int getScreenDPI() {
- DisplayMetrics metrics = activity.getApplicationContext().getResources().getDisplayMetrics();
- return (int)(metrics.density * 160f);
+ return activity.getResources().getDisplayMetrics().densityDpi;
}
- public int[] screenGetUsableRect() {
+ /**
+ * Returns bucketized density values.
+ */
+ public float getScaledDensity() {
+ int densityDpi = activity.getResources().getDisplayMetrics().densityDpi;
+ float selectedScaledDensity;
+ if (densityDpi >= DisplayMetrics.DENSITY_XXXHIGH) {
+ selectedScaledDensity = 4.0f;
+ } else if (densityDpi >= DisplayMetrics.DENSITY_XXHIGH) {
+ selectedScaledDensity = 3.0f;
+ } else if (densityDpi >= DisplayMetrics.DENSITY_XHIGH) {
+ selectedScaledDensity = 2.0f;
+ } else if (densityDpi >= DisplayMetrics.DENSITY_HIGH) {
+ selectedScaledDensity = 1.5f;
+ } else if (densityDpi >= DisplayMetrics.DENSITY_MEDIUM) {
+ selectedScaledDensity = 1.0f;
+ } else {
+ selectedScaledDensity = 0.75f;
+ }
+ Log.d(TAG, "Selected scaled density: " + selectedScaledDensity);
+ return selectedScaledDensity;
+ }
+
+ public double getScreenRefreshRate(double fallback) {
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ if (display != null) {
+ return display.getRefreshRate();
+ }
+ return fallback;
+ }
+
+ public int[] getDisplaySafeArea() {
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
@@ -248,6 +280,25 @@ public class GodotIO {
return result;
}
+ public int[] getDisplayCutouts() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
+ return new int[0];
+ DisplayCutout cutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
+ if (cutout == null)
+ return new int[0];
+ List<Rect> rects = cutout.getBoundingRects();
+ int cutouts = rects.size();
+ int[] result = new int[cutouts * 4];
+ int index = 0;
+ for (Rect rect : rects) {
+ result[index++] = rect.left;
+ result[index++] = rect.top;
+ result[index++] = rect.width();
+ result[index++] = rect.height();
+ }
+ return result;
+ }
+
public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (edit != null)
edit.showKeyboard(p_existing_text, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
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 a23d030d4c..1f8f8c82a6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,8 @@
package org.godotengine.godot;
+import org.godotengine.godot.gl.GodotRenderer;
+
import android.app.Activity;
import android.hardware.SensorEvent;
import android.view.Surface;
@@ -68,7 +70,7 @@ public class GodotLib {
* @param p_surface
* @param p_width
* @param p_height
- * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)
+ * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)
*/
public static native void resize(Surface p_surface, int p_width, int p_height);
@@ -85,9 +87,14 @@ public class GodotLib {
/**
* Invoked on the GL thread to draw the current frame.
- * @see android.opengl.GLSurfaceView.Renderer#onDrawFrame(GL10)
+ * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onDrawFrame(GL10)
+ */
+ public static native boolean step();
+
+ /**
+ * TTS callback.
*/
- public static native void step();
+ public static native void ttsCallback(int event, int id, int pos);
/**
* Forward touch events from the main thread to the GL thread.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 79007764a7..cb63fd885f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 6fca7f2a57..c386a2d2eb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java
new file mode 100644
index 0000000000..af16cfce74
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java
@@ -0,0 +1,566 @@
+// clang-format off
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.gl;
+
+import android.opengl.GLDebugHelper;
+import android.opengl.GLException;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.microedition.khronos.egl.EGL;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+class EGLLogWrapper implements EGL11 {
+ private EGL10 mEgl10;
+ Writer mLog;
+ boolean mLogArgumentNames;
+ boolean mCheckError;
+ private int mArgCount;
+
+
+ public EGLLogWrapper(EGL egl, int configFlags, Writer log) {
+ mEgl10 = (EGL10) egl;
+ mLog = log;
+ mLogArgumentNames =
+ (GLDebugHelper.CONFIG_LOG_ARGUMENT_NAMES & configFlags) != 0;
+ mCheckError =
+ (GLDebugHelper.CONFIG_CHECK_GL_ERROR & configFlags) != 0;
+ }
+
+ public boolean eglChooseConfig(EGLDisplay display, int[] attrib_list,
+ EGLConfig[] configs, int config_size, int[] num_config) {
+ begin("eglChooseConfig");
+ arg("display", display);
+ arg("attrib_list", attrib_list);
+ arg("config_size", config_size);
+ end();
+
+ boolean result = mEgl10.eglChooseConfig(display, attrib_list, configs,
+ config_size, num_config);
+ arg("configs", configs);
+ arg("num_config", num_config);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface,
+ Object native_pixmap) {
+ begin("eglCopyBuffers");
+ arg("display", display);
+ arg("surface", surface);
+ arg("native_pixmap", native_pixmap);
+ end();
+
+ boolean result = mEgl10.eglCopyBuffers(display, surface, native_pixmap);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config,
+ EGLContext share_context, int[] attrib_list) {
+ begin("eglCreateContext");
+ arg("display", display);
+ arg("config", config);
+ arg("share_context", share_context);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLContext result = mEgl10.eglCreateContext(display, config,
+ share_context, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreatePbufferSurface(EGLDisplay display,
+ EGLConfig config, int[] attrib_list) {
+ begin("eglCreatePbufferSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreatePbufferSurface(display, config,
+ attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreatePixmapSurface(EGLDisplay display,
+ EGLConfig config, Object native_pixmap, int[] attrib_list) {
+ begin("eglCreatePixmapSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("native_pixmap", native_pixmap);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreatePixmapSurface(display, config,
+ native_pixmap, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreateWindowSurface(EGLDisplay display,
+ EGLConfig config, Object native_window, int[] attrib_list) {
+ begin("eglCreateWindowSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("native_window", native_window);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreateWindowSurface(display, config,
+ native_window, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglDestroyContext(EGLDisplay display, EGLContext context) {
+ begin("eglDestroyContext");
+ arg("display", display);
+ arg("context", context);
+ end();
+
+ boolean result = mEgl10.eglDestroyContext(display, context);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglDestroySurface(EGLDisplay display, EGLSurface surface) {
+ begin("eglDestroySurface");
+ arg("display", display);
+ arg("surface", surface);
+ end();
+
+ boolean result = mEgl10.eglDestroySurface(display, surface);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config,
+ int attribute, int[] value) {
+ begin("eglGetConfigAttrib");
+ arg("display", display);
+ arg("config", config);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglGetConfigAttrib(display, config, attribute,
+ value);
+ arg("value", value);
+ returns(result);
+ checkError();
+ return false;
+ }
+
+ public boolean eglGetConfigs(EGLDisplay display, EGLConfig[] configs,
+ int config_size, int[] num_config) {
+ begin("eglGetConfigs");
+ arg("display", display);
+ arg("config_size", config_size);
+ end();
+
+ boolean result = mEgl10.eglGetConfigs(display, configs, config_size,
+ num_config);
+ arg("configs", configs);
+ arg("num_config", num_config);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLContext eglGetCurrentContext() {
+ begin("eglGetCurrentContext");
+ end();
+
+ EGLContext result = mEgl10.eglGetCurrentContext();
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLDisplay eglGetCurrentDisplay() {
+ begin("eglGetCurrentDisplay");
+ end();
+
+ EGLDisplay result = mEgl10.eglGetCurrentDisplay();
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglGetCurrentSurface(int readdraw) {
+ begin("eglGetCurrentSurface");
+ arg("readdraw", readdraw);
+ end();
+
+ EGLSurface result = mEgl10.eglGetCurrentSurface(readdraw);
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLDisplay eglGetDisplay(Object native_display) {
+ begin("eglGetDisplay");
+ arg("native_display", native_display);
+ end();
+
+ EGLDisplay result = mEgl10.eglGetDisplay(native_display);
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public int eglGetError() {
+ begin("eglGetError");
+ end();
+
+ int result = mEgl10.eglGetError();
+ returns(getErrorString(result));
+
+ return result;
+ }
+
+ public boolean eglInitialize(EGLDisplay display, int[] major_minor) {
+ begin("eglInitialize");
+ arg("display", display);
+ end();
+ boolean result = mEgl10.eglInitialize(display, major_minor);
+ returns(result);
+ arg("major_minor", major_minor);
+ checkError();
+ return result;
+ }
+
+ public boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw,
+ EGLSurface read, EGLContext context) {
+ begin("eglMakeCurrent");
+ arg("display", display);
+ arg("draw", draw);
+ arg("read", read);
+ arg("context", context);
+ end();
+ boolean result = mEgl10.eglMakeCurrent(display, draw, read, context);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglQueryContext(EGLDisplay display, EGLContext context,
+ int attribute, int[] value) {
+ begin("eglQueryContext");
+ arg("display", display);
+ arg("context", context);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglQueryContext(display, context, attribute,
+ value);
+ returns(value[0]);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public String eglQueryString(EGLDisplay display, int name) {
+ begin("eglQueryString");
+ arg("display", display);
+ arg("name", name);
+ end();
+ String result = mEgl10.eglQueryString(display, name);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglQuerySurface(EGLDisplay display, EGLSurface surface,
+ int attribute, int[] value) {
+ begin("eglQuerySurface");
+ arg("display", display);
+ arg("surface", surface);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglQuerySurface(display, surface, attribute,
+ value);
+ returns(value[0]);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
+ begin("eglSwapBuffers");
+ arg("display", display);
+ arg("surface", surface);
+ end();
+ boolean result = mEgl10.eglSwapBuffers(display, surface);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglTerminate(EGLDisplay display) {
+ begin("eglTerminate");
+ arg("display", display);
+ end();
+ boolean result = mEgl10.eglTerminate(display);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglWaitGL() {
+ begin("eglWaitGL");
+ end();
+ boolean result = mEgl10.eglWaitGL();
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglWaitNative(int engine, Object bindTarget) {
+ begin("eglWaitNative");
+ arg("engine", engine);
+ arg("bindTarget", bindTarget);
+ end();
+ boolean result = mEgl10.eglWaitNative(engine, bindTarget);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ private void checkError() {
+ int eglError;
+ if ((eglError = mEgl10.eglGetError()) != EGL_SUCCESS) {
+ String errorMessage = "eglError: " + getErrorString(eglError);
+ logLine(errorMessage);
+ if (mCheckError) {
+ throw new GLException(eglError, errorMessage);
+ }
+ }
+ }
+
+ private void logLine(String message) {
+ log(message + '\n');
+ }
+
+ private void log(String message) {
+ try {
+ mLog.write(message);
+ } catch (IOException e) {
+ // Ignore exception, keep on trying
+ }
+ }
+
+ private void begin(String name) {
+ log(name + '(');
+ mArgCount = 0;
+ }
+
+ private void arg(String name, String value) {
+ if (mArgCount++ > 0) {
+ log(", ");
+ }
+ if (mLogArgumentNames) {
+ log(name + "=");
+ }
+ log(value);
+ }
+
+ private void end() {
+ log(");\n");
+ flush();
+ }
+
+ private void flush() {
+ try {
+ mLog.flush();
+ } catch (IOException e) {
+ mLog = null;
+ }
+ }
+
+ private void arg(String name, int value) {
+ arg(name, Integer.toString(value));
+ }
+
+ private void arg(String name, Object object) {
+ arg(name, toString(object));
+ }
+
+ private void arg(String name, EGLDisplay object) {
+ if (object == EGL10.EGL_DEFAULT_DISPLAY) {
+ arg(name, "EGL10.EGL_DEFAULT_DISPLAY");
+ } else if (object == EGL_NO_DISPLAY) {
+ arg(name, "EGL10.EGL_NO_DISPLAY");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void arg(String name, EGLContext object) {
+ if (object == EGL10.EGL_NO_CONTEXT) {
+ arg(name, "EGL10.EGL_NO_CONTEXT");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void arg(String name, EGLSurface object) {
+ if (object == EGL10.EGL_NO_SURFACE) {
+ arg(name, "EGL10.EGL_NO_SURFACE");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void returns(String result) {
+ log(" returns " + result + ";\n");
+ flush();
+ }
+
+ private void returns(int result) {
+ returns(Integer.toString(result));
+ }
+
+ private void returns(boolean result) {
+ returns(Boolean.toString(result));
+ }
+
+ private void returns(Object result) {
+ returns(toString(result));
+ }
+
+ private String toString(Object obj) {
+ if (obj == null) {
+ return "null";
+ } else {
+ return obj.toString();
+ }
+ }
+
+ private void arg(String name, int[] arr) {
+ if (arr == null) {
+ arg(name, "null");
+ } else {
+ arg(name, toString(arr.length, arr, 0));
+ }
+ }
+
+ private void arg(String name, Object[] arr) {
+ if (arr == null) {
+ arg(name, "null");
+ } else {
+ arg(name, toString(arr.length, arr, 0));
+ }
+ }
+
+ private String toString(int n, int[] arr, int offset) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("{\n");
+ int arrLen = arr.length;
+ for (int i = 0; i < n; i++) {
+ int index = offset + i;
+ buf.append(" [" + index + "] = ");
+ if (index < 0 || index >= arrLen) {
+ buf.append("out of bounds");
+ } else {
+ buf.append(arr[index]);
+ }
+ buf.append('\n');
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ private String toString(int n, Object[] arr, int offset) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("{\n");
+ int arrLen = arr.length;
+ for (int i = 0; i < n; i++) {
+ int index = offset + i;
+ buf.append(" [" + index + "] = ");
+ if (index < 0 || index >= arrLen) {
+ buf.append("out of bounds");
+ } else {
+ buf.append(arr[index]);
+ }
+ buf.append('\n');
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ private static String getHex(int value) {
+ return "0x" + Integer.toHexString(value);
+ }
+
+ public static String getErrorString(int error) {
+ switch (error) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL11.EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return getHex(error);
+ }
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
new file mode 100644
index 0000000000..8449c08b88
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -0,0 +1,1939 @@
+// clang-format off
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.gl;
+
+import android.content.Context;
+import android.opengl.EGL14;
+import android.opengl.EGLExt;
+import android.opengl.GLDebugHelper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.io.Writer;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying OpenGL rendering.
+ * <p>
+ * A GLSurfaceView provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Manages an EGL display, which enables OpenGL to render into a surface.
+ * <li>Accepts a user-provided Renderer object that does the actual rendering.
+ * <li>Renders on a dedicated thread to decouple rendering performance from the
+ * UI thread.
+ * <li>Supports both on-demand and continuous rendering.
+ * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
+ * </ul>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Using GLSurfaceView</h3>
+ * <p>
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ * <p>
+ * <h3>Initializing GLSurfaceView</h3>
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ * <ul>
+ * <li>{@link #setDebugFlags(int)}
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * <li>{@link #setGLWrapper(GLWrapper)}
+ * </ul>
+ * <p>
+ * <h4>Specifying the android.view.Surface</h4>
+ * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
+ * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
+ * The exact format of a TRANSLUCENT surface is device dependent, but it will be
+ * a 32-bit-per-pixel surface with 8 bits per component.
+ * <p>
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple EGLConfig rendering configurations.
+ * The available configurations may differ in how many channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
+ * <p>
+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
+ * with at least a 16-bit depth buffer and no stencil.
+ * <p>
+ * If you would prefer a different EGLConfig
+ * you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ * <p>
+ * <h4>Debug Behavior</h4>
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * <p>
+ * <h4>Setting a Renderer</h4>
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * <p>
+ * <h3>Rendering Mode</h3>
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * <p>
+ * <h3>Activity Life-cycle</h3>
+ * A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity stops and
+ * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * <p>
+ * <h3>Handling events</h3>
+ * <p>
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ * <pre class="prettyprint">
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
+ private final static String TAG = "GLSurfaceView";
+ private final static boolean LOG_ATTACH_DETACH = false;
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_PAUSE_RESUME = false;
+ private final static boolean LOG_SURFACE = false;
+ private final static boolean LOG_RENDERER = false;
+ private final static boolean LOG_RENDERER_DRAW_FRAME = false;
+ private final static boolean LOG_EGL = false;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGLThread != null) {
+ // GLThread may still be running if this view was never
+ // attached to a window.
+ mGLThread.requestExitAndWait();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+ // this statement if back-porting to 2.2 or older:
+ // holder.setFormat(PixelFormat.RGB_565);
+ //
+ // setType is not needed for SDK 2.0 or newer. Uncomment this
+ // statement if back-porting this code to older SDKs.
+ // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ * <p>
+ * Wrapping is typically used for debugging purposes.
+ * <p>
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Control whether the EGL context is preserved when the GLSurfaceView is paused and
+ * resumed.
+ * <p>
+ * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
+ * <p>
+ * Prior to API level 11, whether the EGL context is actually preserved or not
+ * depends upon whether the Android device can support an arbitrary number of
+ * EGL contexts or not. Devices that can only support a limited number of EGL
+ * contexts must release the EGL context in order to allow multiple applications
+ * to share the GPU.
+ * <p>
+ * If set to false, the EGL context will be released when the GLSurfaceView is paused,
+ * and recreated when the GLSurfaceView is resumed.
+ * <p>
+ *
+ * The default is false.
+ *
+ * @param preserveOnPause preserve the EGL context when paused
+ */
+ public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
+ mPreserveEGLContextOnPause = preserveOnPause;
+ }
+
+ /**
+ * @return true if the EGL context will be preserved when paused
+ */
+ public boolean getPreserveEGLContextOnPause() {
+ return mPreserveEGLContextOnPause;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * <p>This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * <p>The following GLSurfaceView methods can only be called <em>before</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ * <p>
+ * The following GLSurfaceView methods can only be called <em>after</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #getRenderMode()}
+ * <li>{@link #onPause()}
+ * <li>{@link #onResume()}
+ * <li>{@link #queueEvent(Runnable)}
+ * <li>{@link #requestRender()}
+ * <li>{@link #setRenderMode(int)}
+ * </ul>
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ checkRenderThreadState();
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ if (mEGLContextFactory == null) {
+ mEGLContextFactory = new DefaultContextFactory();
+ }
+ if (mEGLWindowSurfaceFactory == null) {
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+ }
+ mRenderer = renderer;
+ mGLThread = new GLThread(mThisWeakRef);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLContextFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a context will be created with no shared context and
+ * with a null attribute list.
+ */
+ public void setEGLContextFactory(EGLContextFactory factory) {
+ checkRenderThreadState();
+ mEGLContextFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLWindowSurfaceFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a window surface will be created with a null attribute list.
+ */
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+ checkRenderThreadState();
+ mEGLWindowSurfaceFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an EGLConfig that is compatible with the current
+ * android.view.Surface, with a depth buffer depth of
+ * at least 16 bits.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ checkRenderThreadState();
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified depthSize and stencilSize,
+ * and exactly the specified redSize, greenSize, blueSize and alphaSize.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+
+ /**
+ * Inform the default EGLContextFactory and default EGLConfigChooser
+ * which EGLContext client version to pick.
+ * <p>Use this method to create an OpenGL ES 2.0-compatible context.
+ * Example:
+ * <pre class="prettyprint">
+ * public MyView(Context context) {
+ * super(context);
+ * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+ * setRenderer(new MyRenderer());
+ * }
+ * </pre>
+ * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
+ * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
+ * AndroidManifest.xml file.
+ * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>This method only affects the behavior of the default EGLContexFactory and the
+ * default EGLConfigChooser. If
+ * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
+ * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
+ * If
+ * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
+ * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
+ */
+ public void setEGLContextClientVersion(int version) {
+ checkRenderThreadState();
+ mEGLContextClientVersion = version;
+ }
+
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ * <p>
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ * <p>
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ @Override
+ public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) {
+ if (mGLThread != null) {
+ mGLThread.requestRenderAndNotify(finishDrawing);
+ }
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ @Deprecated
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ // Since we are part of the framework we know only surfaceRedrawNeededAsync
+ // will be called.
+ }
+
+
+ /**
+ * Pause the rendering thread, optionally tearing down the EGL context
+ * depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}.
+ *
+ * This method should be called when it is no longer desirable for the
+ * GLSurfaceView to continue rendering, such as in response to
+ * {@link android.app.Activity#onStop Activity.onStop}.
+ *
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Resumes the rendering thread, re-creating the OpenGL context if necessary. It
+ * is the counterpart to {@link #onPause()}.
+ *
+ * This method should typically be called in
+ * {@link android.app.Activity#onStart Activity.onStart}.
+ *
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ */
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
+ }
+ if (mDetached && (mRenderer != null)) {
+ int renderMode = RENDERMODE_CONTINUOUSLY;
+ if (mGLThread != null) {
+ renderMode = mGLThread.getRenderMode();
+ }
+ mGLThread = new GLThread(mThisWeakRef);
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
+ mGLThread.setRenderMode(renderMode);
+ }
+ mGLThread.start();
+ }
+ mDetached = false;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onDetachedFromWindow");
+ }
+ if (mGLThread != null) {
+ mGLThread.requestExitAndWait();
+ }
+ mDetached = true;
+ super.onDetachedFromWindow();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * <p>Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ * <pre class="prettyprint">
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ * </pre>
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * <p>
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ * <p>
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ * <p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Threading</h3>
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * <p>
+ * <h3>EGL Context Lost</h3>
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public interface Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * <p>
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The EGL context will typically
+ * be lost when the Android device awakes after going to sleep.
+ * <p>
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ * <p>
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ * <p>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ * <p>
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ * <p>
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ * <pre class="prettyprint">
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(GL10 gl, int width, int height);
+
+ // -- GODOT start --
+ /**
+ * Called to draw the current frame.
+ * <p>
+ * This method is responsible for drawing the current frame.
+ * <p>
+ * The implementation of this method typically looks like this:
+ * <pre class="prettyprint">
+ * boolean onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * return true;
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ *
+ * @return true if the buffers should be swapped, false otherwise.
+ */
+ boolean onDrawFrame(GL10 gl);
+ // -- GODOT end --
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+ */
+ public interface EGLContextFactory {
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+ }
+
+ private class DefaultContextFactory implements EGLContextFactory {
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ EGL10.EGL_NONE };
+
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
+ mEGLContextClientVersion != 0 ? attrib_list : null);
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ if (!egl.eglDestroyContext(display, context)) {
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ if (LOG_THREADS) {
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ }
+ EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
+ }
+ }
+ }
+
+ /**
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+ */
+ public interface EGLWindowSurfaceFactory {
+ /**
+ * @return null if the surface cannot be constructed.
+ */
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+ Object nativeWindow);
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+ }
+
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+ EGLConfig config, Object nativeWindow) {
+ EGLSurface result = null;
+ try {
+ result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+ } catch (IllegalArgumentException e) {
+ // This exception indicates that the surface flinger surface
+ // is not valid. This can happen if the surface flinger surface has
+ // been torn down, but the application has not yet been
+ // notified via SurfaceHolder.Callback.surfaceDestroyed.
+ // In theory the application should be notified first,
+ // but in practice sometimes it is not. See b/4588890
+ Log.e(TAG, "eglCreateWindowSurface", e);
+ }
+ return result;
+ }
+
+ public void destroySurface(EGL10 egl, EGLDisplay display,
+ EGLSurface surface) {
+ egl.eglDestroySurface(display, surface);
+ }
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = filterConfigSpec(configSpec);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+
+ private int[] filterConfigSpec(int[] configSpec) {
+ if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) {
+ return configSpec;
+ }
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+ * And we know the configSpec is well formed.
+ */
+ int len = configSpec.length;
+ int[] newConfigSpec = new int[len + 2];
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+ if (mEGLContextClientVersion == 2) {
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT; /* EGL_OPENGL_ES2_BIT */
+ } else {
+ newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */
+ }
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
+ return newConfigSpec;
+ }
+ }
+
+ /**
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
+ * and at least the specified depth and stencil sizes.
+ */
+ private class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ for (EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if ((d >= mDepthSize) && (s >= mStencilSize)) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ if ((r == mRedSize) && (g == mGreenSize)
+ && (b == mBlueSize) && (a == mAlphaSize)) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a RGB_888 surface with
+ * or without a depth buffer.
+ *
+ */
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private static class EglHelper {
+ public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ */
+ public void start() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view == null) {
+ mEglConfig = null;
+ mEglContext = null;
+ } else {
+ mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an EGL context. We want to do this as rarely as we can, because an
+ * EGL context is a somewhat heavy object.
+ */
+ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ }
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ mEglContext = null;
+ throwEglException("createContext");
+ }
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ }
+
+ mEglSurface = null;
+ }
+
+ /**
+ * Create an egl surface for the current SurfaceHolder surface. If a surface
+ * already exists, destroy it before creating the new surface.
+ *
+ * @return true if the surface was created successfully.
+ */
+ public boolean createSurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Check preconditions.
+ */
+ if (mEgl == null) {
+ throw new RuntimeException("egl not initialized");
+ }
+ if (mEglDisplay == null) {
+ throw new RuntimeException("eglDisplay not initialized");
+ }
+ if (mEglConfig == null) {
+ throw new RuntimeException("mEglConfig not initialized");
+ }
+
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ destroySurfaceImp();
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, view.getHolder());
+ } else {
+ mEglSurface = null;
+ }
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+ Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ }
+ return false;
+ }
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ /*
+ * Could not make the context current, probably because the underlying
+ * SurfaceView surface has been destroyed.
+ */
+ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create a GL object for the current EGL context.
+ * @return
+ */
+ GL createGL() {
+
+ GL gl = mEglContext.getGL();
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (view.mGLWrapper != null) {
+ gl = view.mGLWrapper.wrap(gl);
+ }
+
+ if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
+ int configFlags = 0;
+ Writer log = null;
+ if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+ }
+ if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+ log = new LogWriter();
+ }
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
+ }
+ }
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return the EGL error code from eglSwapBuffers.
+ */
+ public int swap() {
+ if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ return mEgl.eglGetError();
+ }
+ return EGL10.EGL_SUCCESS;
+ }
+
+ public void destroySurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ }
+ destroySurfaceImp();
+ }
+
+ private void destroySurfaceImp() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ }
+ if (mEglContext != null) {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ }
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throwEglException(function, mEgl.eglGetError());
+ }
+
+ public static void throwEglException(String function, int error) {
+ String message = formatEglError(function, error);
+ if (LOG_THREADS) {
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ + message);
+ }
+ throw new RuntimeException(message);
+ }
+
+ public static void logEglErrorAsWarning(String tag, String function, int error) {
+ Log.w(tag, formatEglError(function, error));
+ }
+
+ public static String formatEglError(String function, int error) {
+ return function + " failed: " + EGLLogWrapper.getErrorString(error);
+ }
+
+ private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+
+ }
+
+ /**
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+ * to a Renderer instance to do the actual drawing. Can be configured to
+ * render continuously or on request.
+ *
+ * All potentially blocking synchronization is done through the
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
+ *
+ */
+ static class GLThread extends Thread {
+ GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mWantRenderNotification = false;
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
+ try {
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
+ } finally {
+ sGLThreadManager.threadExiting(this);
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglSurfaceLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
+ mEglHelper.destroySurface();
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
+ if (mHaveEglContext) {
+ mEglHelper.finish();
+ mHaveEglContext = false;
+ sGLThreadManager.releaseEglContextLocked(this);
+ }
+ }
+ private void guardedRun() throws InterruptedException {
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
+ mWantRenderNotification = false;
+
+ try {
+ GL10 gl = null;
+ boolean createEglContext = false;
+ boolean createEglSurface = false;
+ boolean createGlInterface = false;
+ boolean lostEglContext = false;
+ boolean sizeChanged = false;
+ boolean wantRenderNotification = false;
+ boolean doRenderNotification = false;
+ boolean askedToReleaseEglContext = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+ Runnable finishDrawingRunnable = null;
+
+ while (true) {
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Update the pause state.
+ boolean pausing = false;
+ if (mPaused != mRequestPaused) {
+ pausing = mRequestPaused;
+ mPaused = mRequestPaused;
+ sGLThreadManager.notifyAll();
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
+ }
+ }
+
+ // Do we need to give up the EGL context?
+ if (mShouldReleaseEglContext) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ mShouldReleaseEglContext = false;
+ askedToReleaseEglContext = true;
+ }
+
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
+
+ // When pausing, release the EGL surface:
+ if (pausing && mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ }
+
+ // When pausing, optionally release the EGL Context:
+ if (pausing && mHaveEglContext) {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ boolean preserveEglContextOnPause = view == null ?
+ false : view.mPreserveEGLContextOnPause;
+ if (!preserveEglContextOnPause) {
+ stopEglContextLocked();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+ }
+ }
+ }
+
+ // Have we lost the SurfaceView surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEglSurface) {
+ stopEglSurfaceLocked();
+ }
+ mWaitingForSurface = true;
+ mSurfaceIsBad = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Have we acquired the surface view surface?
+ if (mHasSurface && mWaitingForSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+ }
+ mWaitingForSurface = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (doRenderNotification) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "sending render notification tid=" + getId());
+ }
+ mWantRenderNotification = false;
+ doRenderNotification = false;
+ mRenderComplete = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mFinishDrawingRunnable != null) {
+ finishDrawingRunnable = mFinishDrawingRunnable;
+ mFinishDrawingRunnable = null;
+ }
+
+ // Ready to draw?
+ if (readyToDraw()) {
+
+ // If we don't have an EGL context, try to acquire one.
+ if (! mHaveEglContext) {
+ if (askedToReleaseEglContext) {
+ askedToReleaseEglContext = false;
+ } else {
+ try {
+ mEglHelper.start();
+ } catch (RuntimeException t) {
+ sGLThreadManager.releaseEglContextLocked(this);
+ throw t;
+ }
+ mHaveEglContext = true;
+ createEglContext = true;
+
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
+ createEglSurface = true;
+ createGlInterface = true;
+ sizeChanged = true;
+ }
+
+ if (mHaveEglSurface) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ mWantRenderNotification = true;
+ if (LOG_SURFACE) {
+ Log.i("GLThread",
+ "noticing that we want render notification tid="
+ + getId());
+ }
+
+ // Destroy and recreate the EGL surface.
+ createEglSurface = true;
+
+ mSizeChanged = false;
+ }
+ mRequestRender = false;
+ sGLThreadManager.notifyAll();
+ if (mWantRenderNotification) {
+ wantRenderNotification = true;
+ }
+ break;
+ }
+ } else {
+ if (finishDrawingRunnable != null) {
+ Log.w(TAG, "Warning, !readyToDraw() but waiting for " +
+ "draw finished! Early reporting draw finished.");
+ finishDrawingRunnable.run();
+ finishDrawingRunnable = null;
+ }
+ }
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.i("GLThread", "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mSurfaceIsBad
+ + " mWaitingForSurface: " + mWaitingForSurface
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRequestRender: " + mRequestRender
+ + " mRenderMode: " + mRenderMode);
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ continue;
+ }
+
+ if (createEglSurface) {
+ if (LOG_SURFACE) {
+ Log.w("GLThread", "egl createSurface");
+ }
+ if (mEglHelper.createSurface()) {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+ } else {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ continue;
+ }
+ createEglSurface = false;
+ }
+
+ if (createGlInterface) {
+ gl = (GL10) mEglHelper.createGL();
+
+ createGlInterface = false;
+ }
+
+ // -- GODOT start --
+ if (createEglContext) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ } finally {
+ }
+ }
+ createEglContext = false;
+ }
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ view.mRenderer.onSurfaceChanged(gl, w, h);
+ } finally {
+ }
+ }
+ sizeChanged = false;
+ }
+
+ boolean swapBuffers = false;
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
+ }
+ {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ swapBuffers = view.mRenderer.onDrawFrame(gl);
+ if (finishDrawingRunnable != null) {
+ finishDrawingRunnable.run();
+ finishDrawingRunnable = null;
+ }
+ } finally {}
+ }
+ }
+ if (swapBuffers) {
+ int swapError = mEglHelper.swap();
+ switch (swapError) {
+ case EGL10.EGL_SUCCESS:
+ break;
+ case EGL11.EGL_CONTEXT_LOST:
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "egl context lost tid=" + getId());
+ }
+ lostEglContext = true;
+ break;
+ default:
+ // Other errors typically mean that the current surface is bad,
+ // probably because the SurfaceView surface has been destroyed,
+ // but we haven't been notified yet.
+ // Log the error to help developers understand why rendering stopped.
+ EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+
+ synchronized (sGLThreadManager) {
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ break;
+ }
+ }
+ // -- GODOT end --
+
+ if (wantRenderNotification) {
+ doRenderNotification = true;
+ wantRenderNotification = false;
+ }
+ }
+
+ } finally {
+ /*
+ * clean-up everything...
+ */
+ synchronized (sGLThreadManager) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ }
+ }
+ }
+
+ public boolean ableToDraw() {
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
+ }
+
+ private boolean readyToDraw() {
+ return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void requestRenderAndNotify(Runnable finishDrawing) {
+ synchronized(sGLThreadManager) {
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We will return to the client rendering code, so here we don't need to
+ // do anything.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
+ mWantRenderNotification = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+ mFinishDrawingRunnable = finishDrawing;
+
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
+ }
+ mHasSurface = true;
+ mFinishedCreatingEglSurface = false;
+ sGLThreadManager.notifyAll();
+ while (mWaitingForSurface
+ && !mFinishedCreatingEglSurface
+ && !mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onPause tid=" + getId());
+ }
+ mRequestPaused = true;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && (! mPaused)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onPause waiting for mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onResume tid=" + getId());
+ }
+ mRequestPaused = false;
+ mRequestRender = true;
+ mRenderComplete = false;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && mPaused && (!mRenderComplete)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onResume waiting for !mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We need to process the size change eventually though and update our EGLSurface.
+ // So we set the parameters and return so they can be processed on our
+ // next iteration.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
+ sGLThreadManager.notifyAll();
+
+ // Wait for thread to react to resize and render a frame
+ while (! mExited && !mPaused && !mRenderComplete
+ && ableToDraw()) {
+ if (LOG_SURFACE) {
+ Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(sGLThreadManager) {
+ mShouldExit = true;
+ sGLThreadManager.notifyAll();
+ while (! mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestReleaseEglContextLocked() {
+ mShouldReleaseEglContext = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ /**
+ * Queue an "event" to be run on the GL rendering thread.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ // Once the thread is started, all accesses to the following member
+ // variables are protected by the sGLThreadManager monitor
+ private boolean mShouldExit;
+ private boolean mExited;
+ private boolean mRequestPaused;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private boolean mSurfaceIsBad;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
+ private boolean mFinishedCreatingEglSurface;
+ private boolean mShouldReleaseEglContext;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private boolean mWantRenderNotification;
+ private boolean mRenderComplete;
+ private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+ private boolean mSizeChanged = true;
+ private Runnable mFinishDrawingRunnable = null;
+
+ // End of member variables protected by the sGLThreadManager monitor.
+
+ private EglHelper mEglHelper;
+
+ /**
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ }
+
+ static class LogWriter extends Writer {
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
+ char c = buf[offset + i];
+ if ( c == '\n') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ Log.v("GLSurfaceView", mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+
+ private StringBuilder mBuilder = new StringBuilder();
+ }
+
+
+ private void checkRenderThreadState() {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ }
+
+ private static class GLThreadManager {
+ private static String TAG = "GLThreadManager";
+
+ public synchronized void threadExiting(GLThread thread) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "exiting tid=" + thread.getId());
+ }
+ thread.mExited = true;
+ notifyAll();
+ }
+
+ /*
+ * Releases the EGL context. Requires that we are already in the
+ * sGLThreadManager monitor when this is called.
+ */
+ public void releaseEglContextLocked(GLThread thread) {
+ notifyAll();
+ }
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+}
+
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java
index 12e452fc99..5c4fd00f6d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,38 +28,38 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot;
+package org.godotengine.godot.gl;
+import org.godotengine.godot.GodotLib;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
-import org.godotengine.godot.utils.GLUtils;
-
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
- * Godot's renderer implementation.
+ * Godot's GL renderer implementation.
*/
-class GodotRenderer implements GLSurfaceView.Renderer {
+public class GodotRenderer implements GLSurfaceView.Renderer {
private final GodotPluginRegistry pluginRegistry;
private boolean activityJustResumed = false;
- GodotRenderer() {
+ public GodotRenderer() {
this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();
}
- public void onDrawFrame(GL10 gl) {
+ public boolean onDrawFrame(GL10 gl) {
if (activityJustResumed) {
GodotLib.onRendererResumed();
activityJustResumed = false;
}
- GodotLib.step();
+ boolean swapBuffers = GodotLib.step();
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLDrawFrame(gl);
}
+
+ return swapBuffers;
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
@@ -76,13 +76,13 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
}
- void onActivityResumed() {
+ public void onActivityResumed() {
// We defer invoking GodotLib.onRendererResumed() until the first draw frame call.
// This ensures we have a valid GL context and surface when we do so.
activityJustResumed = true;
}
- void onActivityPaused() {
+ public void onActivityPaused() {
GodotLib.onRendererPaused();
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
index a98ecad594..ecb2af0a7b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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
index 6b248fd034..ac13cad23e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 fc0b84b392..8694bb91e1 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
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,9 @@ import static org.godotengine.godot.utils.GLUtils.DEBUG;
import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
-import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
+import android.content.Context;
+import android.hardware.input.InputManager;
import android.os.Build;
import android.util.Log;
import android.util.SparseArray;
@@ -53,9 +54,9 @@ import java.util.Set;
/**
* Handles input related events for the {@link GodotRenderView} view.
*/
-public class GodotInputHandler implements InputDeviceListener {
+public class GodotInputHandler implements InputManager.InputDeviceListener {
private final GodotRenderView mRenderView;
- private final InputManagerCompat mInputManager;
+ private final InputManager mInputManager;
private final String tag = this.getClass().getSimpleName();
@@ -64,7 +65,7 @@ public class GodotInputHandler implements InputDeviceListener {
public GodotInputHandler(GodotRenderView godotView) {
mRenderView = godotView;
- mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext());
+ mInputManager = (InputManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 002a75277d..e940aafa9e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
deleted file mode 100644
index 21fdc658bb..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.godotengine.godot.input;
-
-import android.content.Context;
-import android.os.Handler;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-
-public interface InputManagerCompat {
- /**
- * Gets information about the input device with the specified id.
- *
- * @param id The device id
- * @return The input device or null if not found
- */
- InputDevice getInputDevice(int id);
-
- /**
- * Gets the ids of all input devices in the system.
- *
- * @return The input device ids.
- */
- int[] getInputDeviceIds();
-
- /**
- * Registers an input device listener to receive notifications about when
- * input devices are added, removed or changed.
- *
- * @param listener The listener to register.
- * @param handler The handler on which the listener should be invoked, or
- * null if the listener should be invoked on the calling thread's
- * looper.
- */
- void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,
- Handler handler);
-
- /**
- * Unregisters an input device listener.
- *
- * @param listener The listener to unregister.
- */
- void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);
-
- /*
- * The following three calls are to simulate V16 behavior on pre-Jellybean
- * devices. If you don't call them, your callback will never be called
- * pre-API 16.
- */
-
- /**
- * Pass the motion events to the InputManagerCompat. This is used to
- * optimize for polling for controllers. If you do not pass these events in,
- * polling will cause regular object creation.
- *
- * @param event the motion event from the app
- */
- void onGenericMotionEvent(MotionEvent event);
-
- /**
- * Tell the V9 input manager that it should stop polling for disconnected
- * devices. You can call this during onPause in your activity, although you
- * might want to call it whenever your game is not active (or whenever you
- * don't care about being notified of new input devices)
- */
- void onPause();
-
- /**
- * Tell the V9 input manager that it should start polling for disconnected
- * devices. You can call this during onResume in your activity, although you
- * might want to call it less often (only when the gameplay is actually
- * active)
- */
- void onResume();
-
- interface InputDeviceListener {
- /**
- * Called whenever the input manager detects that a device has been
- * added. This will only be called in the V9 version when a motion event
- * is detected.
- *
- * @param deviceId The id of the input device that was added.
- */
- void onInputDeviceAdded(int deviceId);
-
- /**
- * Called whenever the properties of an input device have changed since
- * they were last queried. This will not be called for the V9 version of
- * the API.
- *
- * @param deviceId The id of the input device that changed.
- */
- void onInputDeviceChanged(int deviceId);
-
- /**
- * Called whenever the input manager detects that a device has been
- * removed. For the V9 version, this can take some time depending on the
- * poll rate.
- *
- * @param deviceId The id of the input device that was removed.
- */
- void onInputDeviceRemoved(int deviceId);
- }
-
- /**
- * Use this to construct a compatible InputManager.
- */
- class Factory {
- /**
- * Constructs and returns a compatible InputManger
- *
- * @param context the Context that will be used to get the system
- * service from
- * @return a compatible implementation of InputManager
- */
- public static InputManagerCompat getInputManager(Context context) {
- return new InputManagerV16(context);
- }
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
deleted file mode 100644
index 0dbc13c77b..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.godotengine.godot.input;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.os.Build;
-import android.os.Handler;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
-public class InputManagerV16 implements InputManagerCompat {
- private final InputManager mInputManager;
- private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners;
-
- public InputManagerV16(Context context) {
- mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
- mListeners = new HashMap<>();
- }
-
- @Override
- public InputDevice getInputDevice(int id) {
- return mInputManager.getInputDevice(id);
- }
-
- @Override
- public int[] getInputDeviceIds() {
- return mInputManager.getInputDeviceIds();
- }
-
- static class V16InputDeviceListener implements InputManager.InputDeviceListener {
- final InputManagerCompat.InputDeviceListener mIDL;
-
- public V16InputDeviceListener(InputDeviceListener idl) {
- mIDL = idl;
- }
-
- @Override
- public void onInputDeviceAdded(int deviceId) {
- mIDL.onInputDeviceAdded(deviceId);
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- mIDL.onInputDeviceChanged(deviceId);
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- mIDL.onInputDeviceRemoved(deviceId);
- }
- }
-
- @Override
- public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
- V16InputDeviceListener v16Listener = new V16InputDeviceListener(listener);
- mInputManager.registerInputDeviceListener(v16Listener, handler);
- mListeners.put(listener, v16Listener);
- }
-
- @Override
- public void unregisterInputDeviceListener(InputDeviceListener listener) {
- V16InputDeviceListener curListener = mListeners.remove(listener);
- if (null != curListener) {
- mInputManager.unregisterInputDeviceListener(curListener);
- }
- }
-
- @Override
- public void onGenericMotionEvent(MotionEvent event) {
- // unused in V16
- }
-
- @Override
- public void onPause() {
- // unused in V16
- }
-
- @Override
- public void onResume() {
- // unused in V16
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
index bff90d7ce9..bace516b33 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index 4536c21ed3..bb5042fa09 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
index 09366384c2..cfb84c3931 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index 5b41205253..502ea0507d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
index 6428422641..8c7faaa75e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java
index 04c091d944..dc912af63c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
new file mode 100644
index 0000000000..2239ddac8e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
@@ -0,0 +1,298 @@
+/*************************************************************************/
+/* GodotTTS.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.tts;
+
+import org.godotengine.godot.GodotLib;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.UtteranceProgressListener;
+import android.speech.tts.Voice;
+
+import androidx.annotation.Keep;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * Wrapper for Android Text to Speech API and custom utterance query implementation.
+ * <p>
+ * A [GodotTTS] provides the following features:
+ * <p>
+ * <ul>
+ * <li>Access to the Android Text to Speech API.
+ * <li>Utterance pause / resume functions, unsupported by Android TTS API.
+ * </ul>
+ */
+@Keep
+public class GodotTTS extends UtteranceProgressListener {
+ // Note: These constants must be in sync with DisplayServer::TTSUtteranceEvent enum from "servers/display_server.h".
+ final private static int EVENT_START = 0;
+ final private static int EVENT_END = 1;
+ final private static int EVENT_CANCEL = 2;
+ final private static int EVENT_BOUNDARY = 3;
+
+ final private TextToSpeech synth;
+ final private LinkedList<GodotUtterance> queue;
+ final private Object lock = new Object();
+ private GodotUtterance lastUtterance;
+
+ private boolean speaking;
+ private boolean paused;
+
+ public GodotTTS(Activity p_activity) {
+ synth = new TextToSpeech(p_activity, null);
+ queue = new LinkedList<GodotUtterance>();
+
+ synth.setOnUtteranceProgressListener(this);
+ }
+
+ private void updateTTS() {
+ if (!speaking && queue.size() > 0) {
+ int mode = TextToSpeech.QUEUE_FLUSH;
+ GodotUtterance message = queue.pollFirst();
+
+ Set<Voice> voices = synth.getVoices();
+ for (Voice v : voices) {
+ if (v.getName().equals(message.voice)) {
+ synth.setVoice(v);
+ break;
+ }
+ }
+ synth.setPitch(message.pitch);
+ synth.setSpeechRate(message.rate);
+
+ Bundle params = new Bundle();
+ params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, message.volume / 100.f);
+
+ lastUtterance = message;
+ lastUtterance.start = 0;
+ lastUtterance.offset = 0;
+ paused = false;
+
+ synth.speak(message.text, mode, params, String.valueOf(message.id));
+ speaking = true;
+ }
+ }
+
+ /**
+ * Called by TTS engine when the TTS service is about to speak the specified range.
+ */
+ @Override
+ public void onRangeStart(String utteranceId, int start, int end, int frame) {
+ synchronized (lock) {
+ if (lastUtterance != null && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ lastUtterance.offset = start;
+ GodotLib.ttsCallback(EVENT_BOUNDARY, lastUtterance.id, start + lastUtterance.start);
+ }
+ }
+ }
+
+ /**
+ * Called by TTS engine when an utterance was canceled in progress.
+ */
+ @Override
+ public void onStop(String utteranceId, boolean interrupted) {
+ synchronized (lock) {
+ if (lastUtterance != null && !paused && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ GodotLib.ttsCallback(EVENT_CANCEL, lastUtterance.id, 0);
+ speaking = false;
+ updateTTS();
+ }
+ }
+ }
+
+ /**
+ * Called by TTS engine when an utterance has begun to be spoken..
+ */
+ @Override
+ public void onStart(String utteranceId) {
+ synchronized (lock) {
+ if (lastUtterance != null && lastUtterance.start == 0 && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ GodotLib.ttsCallback(EVENT_START, lastUtterance.id, 0);
+ }
+ }
+ }
+
+ /**
+ * Called by TTS engine when an utterance was successfully finished.
+ */
+ @Override
+ public void onDone(String utteranceId) {
+ synchronized (lock) {
+ if (lastUtterance != null && !paused && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ GodotLib.ttsCallback(EVENT_END, lastUtterance.id, 0);
+ speaking = false;
+ updateTTS();
+ }
+ }
+ }
+
+ /**
+ * Called by TTS engine when an error has occurred during processing.
+ */
+ @Override
+ public void onError(String utteranceId, int errorCode) {
+ synchronized (lock) {
+ if (lastUtterance != null && !paused && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ GodotLib.ttsCallback(EVENT_CANCEL, lastUtterance.id, 0);
+ speaking = false;
+ updateTTS();
+ }
+ }
+ }
+
+ /**
+ * Called by TTS engine when an error has occurred during processing (pre API level 21 version).
+ */
+ @Override
+ public void onError(String utteranceId) {
+ synchronized (lock) {
+ if (lastUtterance != null && !paused && Integer.parseInt(utteranceId) == lastUtterance.id) {
+ GodotLib.ttsCallback(EVENT_CANCEL, lastUtterance.id, 0);
+ speaking = false;
+ updateTTS();
+ }
+ }
+ }
+
+ /**
+ * Adds an utterance to the queue.
+ */
+ public void speak(String text, String voice, int volume, float pitch, float rate, int utterance_id, boolean interrupt) {
+ synchronized (lock) {
+ GodotUtterance message = new GodotUtterance(text, voice, volume, pitch, rate, utterance_id);
+ queue.addLast(message);
+
+ if (isPaused()) {
+ resumeSpeaking();
+ } else {
+ updateTTS();
+ }
+ }
+ }
+
+ /**
+ * Puts the synthesizer into a paused state.
+ */
+ public void pauseSpeaking() {
+ synchronized (lock) {
+ if (!paused) {
+ paused = true;
+ synth.stop();
+ }
+ }
+ }
+
+ /**
+ * Resumes the synthesizer if it was paused.
+ */
+ public void resumeSpeaking() {
+ synchronized (lock) {
+ if (lastUtterance != null && paused) {
+ int mode = TextToSpeech.QUEUE_FLUSH;
+
+ Set<Voice> voices = synth.getVoices();
+ for (Voice v : voices) {
+ if (v.getName().equals(lastUtterance.voice)) {
+ synth.setVoice(v);
+ break;
+ }
+ }
+ synth.setPitch(lastUtterance.pitch);
+ synth.setSpeechRate(lastUtterance.rate);
+
+ Bundle params = new Bundle();
+ params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, lastUtterance.volume / 100.f);
+
+ lastUtterance.start = lastUtterance.offset;
+ lastUtterance.offset = 0;
+ paused = false;
+
+ synth.speak(lastUtterance.text.substring(lastUtterance.start), mode, params, String.valueOf(lastUtterance.id));
+ speaking = true;
+ } else {
+ paused = false;
+ }
+ }
+ }
+
+ /**
+ * Stops synthesis in progress and removes all utterances from the queue.
+ */
+ public void stopSpeaking() {
+ synchronized (lock) {
+ for (GodotUtterance u : queue) {
+ GodotLib.ttsCallback(EVENT_CANCEL, u.id, 0);
+ }
+ queue.clear();
+
+ if (lastUtterance != null) {
+ GodotLib.ttsCallback(EVENT_CANCEL, lastUtterance.id, 0);
+ }
+ lastUtterance = null;
+
+ paused = false;
+ speaking = false;
+
+ synth.stop();
+ }
+ }
+
+ /**
+ * Returns voice information.
+ */
+ public String[] getVoices() {
+ Set<Voice> voices = synth.getVoices();
+ String[] list = new String[voices.size()];
+ int i = 0;
+ for (Voice v : voices) {
+ list[i++] = v.getLocale().toString() + ";" + v.getName();
+ }
+ return list;
+ }
+
+ /**
+ * Returns true if the synthesizer is generating speech, or have utterance waiting in the queue.
+ */
+ public boolean isSpeaking() {
+ return speaking;
+ }
+
+ /**
+ * Returns true if the synthesizer is in a paused state.
+ */
+ public boolean isPaused() {
+ return paused;
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotUtterance.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotUtterance.java
new file mode 100644
index 0000000000..bde37e7315
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotUtterance.java
@@ -0,0 +1,55 @@
+/*************************************************************************/
+/* GodotUtterance.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.tts;
+
+/**
+ * A speech request for GodotTTS.
+ */
+class GodotUtterance {
+ final String text;
+ final String voice;
+ final int volume;
+ final float pitch;
+ final float rate;
+ final int id;
+
+ int offset = -1;
+ int start = 0;
+
+ GodotUtterance(String text, String voice, int volume, float pitch, float rate, int id) {
+ this.text = text;
+ this.voice = voice;
+ this.volume = volume;
+ this.pitch = pitch;
+ this.rate = rate;
+ this.id = id;
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
index 2b6e4ad2be..47df23fe1a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,8 +43,8 @@ public class Crypt {
// Create Hex String
StringBuilder hexString = new StringBuilder();
- for (int i = 0; i < messageDigest.length; i++)
- hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+ for (byte b : messageDigest)
+ hexString.append(Integer.toHexString(0xFF & b));
return hexString.toString();
} catch (Exception e) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
index 09820fad5f..4525c5c212 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -147,8 +147,9 @@ public class GLUtils {
Log.i(TAG, String.format(" %s: %d\n", name, value[0]));
} else {
// Log.w(TAG, String.format(" %s: failed\n", name));
- while (egl.eglGetError() != EGL10.EGL_SUCCESS)
- ;
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS) {
+ // Continue.
+ }
}
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
index 721d7c65c6..a17092d3bd 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index b0ca96271e..e5b4f41153 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,8 +45,8 @@ import java.util.List;
/**
* This class includes utility functions for Android permissions related operations.
- * @author Cagdas Caglak <cagdascaglak@gmail.com>
*/
+
public final class PermissionsUtil {
private static final String TAG = PermissionsUtil.class.getSimpleName();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java b/platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
new file mode 100644
index 0000000000..2cc37b627a
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
@@ -0,0 +1,141 @@
+// clang-format off
+
+/* Third-party library.
+ * Upstream: https://github.com/JakeWharton/ProcessPhoenix
+ * Commit: 12cb27c2cc9c3fc555e97f2db89e571667de82c4
+ */
+
+/*
+ * Copyright (C) 2014 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.godotengine.godot.utils;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+/**
+ * Process Phoenix facilitates restarting your application process. This should only be used for
+ * things like fundamental state changes in your debug builds (e.g., changing from staging to
+ * production).
+ * <p>
+ * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.
+ */
+public final class ProcessPhoenix extends Activity {
+ private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents";
+ private static final String KEY_MAIN_PROCESS_PID = "phoenix_main_process_pid";
+
+ /**
+ * Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}
+ * activity as an intent.
+ * <p>
+ * Behavior of the current process after invoking this method is undefined.
+ */
+ public static void triggerRebirth(Context context) {
+ triggerRebirth(context, getRestartIntent(context));
+ }
+
+ /**
+ * Call to restart the application process using the specified intents.
+ * <p>
+ * Behavior of the current process after invoking this method is undefined.
+ */
+ public static void triggerRebirth(Context context, Intent... nextIntents) {
+ if (nextIntents.length < 1) {
+ throw new IllegalArgumentException("intents cannot be empty");
+ }
+ // create a new task for the first activity.
+ nextIntents[0].addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+
+ Intent intent = new Intent(context, ProcessPhoenix.class);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.
+ intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents)));
+ intent.putExtra(KEY_MAIN_PROCESS_PID, Process.myPid());
+ context.startActivity(intent);
+ }
+
+ // -- GODOT start --
+ /**
+ * Finish the activity and kill its process
+ */
+ public static void forceQuit(Activity activity) {
+ forceQuit(activity, Process.myPid());
+ }
+
+ /**
+ * Finish the activity and kill its process
+ * @param activity
+ * @param pid
+ */
+ public static void forceQuit(Activity activity, int pid) {
+ Process.killProcess(pid); // Kill original main process
+ activity.finish();
+ Runtime.getRuntime().exit(0); // Kill kill kill!
+ }
+
+ // -- GODOT end --
+
+ private static Intent getRestartIntent(Context context) {
+ String packageName = context.getPackageName();
+ Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
+ if (defaultIntent != null) {
+ return defaultIntent;
+ }
+
+ throw new IllegalStateException("Unable to determine default activity for "
+ + packageName
+ + ". Does an activity specify the DEFAULT category in its intent filter?");
+ }
+
+ @Override protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // -- GODOT start --
+ ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);
+ startActivities(intents.toArray(new Intent[intents.size()]));
+ forceQuit(this, getIntent().getIntExtra(KEY_MAIN_PROCESS_PID, -1));
+ // -- GODOT end --
+ }
+
+ /**
+ * Checks if the current process is a temporary Phoenix Process.
+ * This can be used to avoid initialisation of unused resources or to prevent running code that
+ * is not multi-process ready.
+ *
+ * @return true if the current process is a temporary Phoenix Process
+ */
+ public static boolean isPhoenixProcess(Context context) {
+ int currentPid = Process.myPid();
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
+ if (runningProcesses != null) {
+ for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
+ if (processInfo.pid == currentPid && processInfo.processName.endsWith(":phoenix")) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index b13f9bfeab..07e22bbcd2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
index b01dc2653a..1581665195 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
index 6e59268076..5ab437f364 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
index 58f02b0396..2cc049de15 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
index 245d573bfc..e35d4f5828 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,9 @@
package org.godotengine.godot.xr.ovr;
+import org.godotengine.godot.gl.GLSurfaceView;
+
import android.opengl.EGLExt;
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
index d3aca267ef..deb9c4bb1d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,9 @@
package org.godotengine.godot.xr.ovr;
+import org.godotengine.godot.gl.GLSurfaceView;
+
import android.opengl.EGL14;
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
index 83aa458064..f087b7dc74 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
package org.godotengine.godot.xr.ovr;
-import android.opengl.GLSurfaceView;
+import org.godotengine.godot.gl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
index 341427209b..445238b1c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,9 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.utils.GLUtils;
-import android.opengl.GLSurfaceView;
-
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index c852e8759a..5d62723170 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.utils.GLUtils;
-import android.opengl.GLSurfaceView;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
index 63c5381994..68329c5c49 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,6 @@
package org.godotengine.godot.xr.regular;
-import org.godotengine.godot.utils.GLUtils;
-
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index 34925684da..711f7cd502 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -15,5 +15,6 @@ file(GLOB_RECURSE HEADERS ${GODOT_ROOT_DIR}/*.h**)
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
- ${GODOT_ROOT_DIR}
- ${GODOT_ROOT_DIR}/modules/gdnative/include)
+ ${GODOT_ROOT_DIR})
+
+add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED)
diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle
index 158bb2b98e..5e810ae1ba 100644
--- a/platform/android/java/nativeSrcsConfigs/build.gradle
+++ b/platform/android/java/nativeSrcsConfigs/build.gradle
@@ -1,11 +1,13 @@
// Non functional android library used to provide Android Studio editor support to the project.
plugins {
id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
}
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
@@ -17,6 +19,10 @@ android {
targetCompatibility versions.javaVersion
}
+ kotlinOptions {
+ jvmTarget = versions.javaVersion
+ }
+
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
@@ -28,8 +34,6 @@ android {
}
}
- ndkVersion versions.ndkVersion
-
externalNativeBuild {
cmake {
path "CMakeLists.txt"
diff --git a/platform/android/java/scripts/publish-module.gradle b/platform/android/java/scripts/publish-module.gradle
new file mode 100644
index 0000000000..32b749e493
--- /dev/null
+++ b/platform/android/java/scripts/publish-module.gradle
@@ -0,0 +1,69 @@
+apply plugin: 'maven-publish'
+apply plugin: 'signing'
+
+group = ossrhGroupId
+version = PUBLISH_VERSION
+
+afterEvaluate {
+ publishing {
+ publications {
+ templateRelease(MavenPublication) {
+ from components.templateRelease
+
+ // The coordinates of the library, being set from variables that
+ // we'll set up later
+ groupId ossrhGroupId
+ artifactId PUBLISH_ARTIFACT_ID
+ version PUBLISH_VERSION
+
+ // Mostly self-explanatory metadata
+ pom {
+ name = PUBLISH_ARTIFACT_ID
+ description = 'Godot Engine Android Library'
+ url = 'https://godotengine.org/'
+ licenses {
+ license {
+ name = 'MIT License'
+ url = 'https://github.com/godotengine/godot/blob/master/LICENSE.txt'
+ }
+ }
+ developers {
+ developer {
+ id = 'm4gr3d'
+ name = 'Fredia Huya-Kouadio'
+ email = 'fhuyakou@gmail.com'
+ }
+ developer {
+ id = 'reduz'
+ name = 'Juan Linietsky'
+ email = 'reduzio@gmail.com'
+ }
+ developer {
+ id = 'akien-mga'
+ name = 'Rémi Verschelde'
+ email = 'rverschelde@gmail.com'
+ }
+ // Add all other devs here...
+ }
+
+ // Version control info - if you're using GitHub, follow the
+ // format as seen here
+ scm {
+ connection = 'scm:git:github.com/godotengine/godot.git'
+ developerConnection = 'scm:git:ssh://github.com/godotengine/godot.git'
+ url = 'https://github.com/godotengine/godot/tree/master'
+ }
+ }
+ }
+ }
+ }
+}
+
+signing {
+ useInMemoryPgpKeys(
+ rootProject.ext["signing.keyId"],
+ rootProject.ext["signing.key"],
+ rootProject.ext["signing.password"],
+ )
+ sign publishing.publications
+}
diff --git a/platform/android/java/scripts/publish-root.gradle b/platform/android/java/scripts/publish-root.gradle
new file mode 100644
index 0000000000..ae88487c34
--- /dev/null
+++ b/platform/android/java/scripts/publish-root.gradle
@@ -0,0 +1,39 @@
+// Create variables with empty default values
+ext["signing.keyId"] = ''
+ext["signing.password"] = ''
+ext["signing.key"] = ''
+ext["ossrhGroupId"] = ''
+ext["ossrhUsername"] = ''
+ext["ossrhPassword"] = ''
+ext["sonatypeStagingProfileId"] = ''
+
+File secretPropsFile = project.rootProject.file('local.properties')
+if (secretPropsFile.exists()) {
+ // Read local.properties file first if it exists
+ Properties p = new Properties()
+ new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
+ p.each { name, value -> ext[name] = value }
+} else {
+ // Use system environment variables
+ ext["ossrhGroupId"] = System.getenv('OSSRH_GROUP_ID')
+ ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
+ ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
+ ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
+ ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
+ ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
+ ext["signing.key"] = System.getenv('SIGNING_KEY')
+}
+
+// Set up Sonatype repository
+nexusPublishing {
+ repositories {
+ sonatype {
+ stagingProfileId = sonatypeStagingProfileId
+ username = ossrhUsername
+ password = ossrhPassword
+ nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
+ snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
+ }
+ }
+}
+
diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle
index 584b626900..466ffebf22 100644
--- a/platform/android/java/settings.gradle
+++ b/platform/android/java/settings.gradle
@@ -1,9 +1,25 @@
// Configure the root project.
+pluginManagement {
+ apply from: 'app/config.gradle'
+
+ plugins {
+ id 'com.android.application' version versions.androidGradlePlugin
+ id 'com.android.library' version versions.androidGradlePlugin
+ id 'org.jetbrains.kotlin.android' version versions.kotlinVersion
+ id 'io.github.gradle-nexus.publish-plugin' version versions.nexusPublishVersion
+ }
+ repositories {
+ gradlePluginPortal()
+ google()
+ }
+}
+
rootProject.name = "Godot"
include ':app'
include ':lib'
include ':nativeSrcsConfigs'
+include ':editor'
include ':assetPacks:installTime'
project(':assetPacks:installTime').projectDir = file("app/assetPacks/installTime")
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index 49891cd739..349ae704f9 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,19 +29,21 @@
/*************************************************************************/
#include "api/java_class_wrapper.h"
+
#include "string_android.h"
#include "thread_jandroid.h"
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
- Map<StringName, List<MethodInfo>>::Element *M = methods.find(p_method);
- if (!M)
+ HashMap<StringName, List<MethodInfo>>::Iterator M = methods.find(p_method);
+ if (!M) {
return false;
+ }
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, false);
+ ERR_FAIL_NULL_V(env, false);
MethodInfo *method = nullptr;
- for (MethodInfo &E : M->get()) {
+ for (MethodInfo &E : M->value) {
if (!p_instance && !E._static) {
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
continue;
@@ -68,8 +70,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
//bug?
} break;
case ARG_TYPE_BOOLEAN: {
- if (p_args[i]->get_type() != Variant::BOOL)
+ if (p_args[i]->get_type() != Variant::BOOL) {
arg_expected = Variant::BOOL;
+ }
} break;
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE:
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR:
@@ -81,27 +84,27 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
case ARG_TYPE_SHORT:
case ARG_TYPE_INT:
case ARG_TYPE_LONG: {
- if (!p_args[i]->is_num())
+ if (!p_args[i]->is_num()) {
arg_expected = Variant::INT;
-
+ }
} break;
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT:
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE:
case ARG_TYPE_FLOAT:
case ARG_TYPE_DOUBLE: {
- if (!p_args[i]->is_num())
+ if (!p_args[i]->is_num()) {
arg_expected = Variant::FLOAT;
-
+ }
} break;
case ARG_TYPE_STRING: {
- if (p_args[i]->get_type() != Variant::STRING)
+ if (p_args[i]->get_type() != Variant::STRING) {
arg_expected = Variant::STRING;
-
+ }
} break;
case ARG_TYPE_CLASS: {
- if (p_args[i]->get_type() != Variant::OBJECT)
+ if (p_args[i]->get_type() != Variant::OBJECT) {
arg_expected = Variant::OBJECT;
- else {
+ } else {
Ref<RefCounted> ref = *p_args[i];
if (!ref.is_null()) {
if (Object::cast_to<JavaObject>(ref.ptr())) {
@@ -118,12 +121,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
}
}
}
-
} break;
default: {
- if (p_args[i]->get_type() != Variant::ARRAY)
+ if (p_args[i]->get_type() != Variant::ARRAY) {
arg_expected = Variant::ARRAY;
-
+ }
} break;
}
@@ -135,15 +137,17 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
break;
}
}
- if (!valid)
+ if (!valid) {
continue;
+ }
method = &E;
break;
}
- if (!method)
+ if (!method) {
return true; //no version convinces
+ }
r_error.error = Callable::CallError::CALL_OK;
@@ -481,14 +485,14 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
return success;
}
-Variant JavaClass::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
Variant ret;
bool found = _call_method(nullptr, p_method, p_args, p_argcount, r_error, ret);
if (found) {
return ret;
}
- return RefCounted::call(p_method, p_args, p_argcount, r_error);
+ return RefCounted::callp(p_method, p_args, p_argcount, r_error);
}
JavaClass::JavaClass() {
@@ -496,7 +500,7 @@ JavaClass::JavaClass() {
/////////////////////
-Variant JavaObject::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
return Variant();
}
@@ -780,9 +784,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
bool val = env->CallBooleanMethod(o, JavaClassWrapper::singleton->Boolean_booleanValue);
ret.push_back(val);
}
@@ -801,9 +805,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
int val = env->CallByteMethod(o, JavaClassWrapper::singleton->Byte_byteValue);
ret.push_back(val);
}
@@ -821,9 +825,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
int val = env->CallCharMethod(o, JavaClassWrapper::singleton->Character_characterValue);
ret.push_back(val);
}
@@ -841,9 +845,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
int val = env->CallShortMethod(o, JavaClassWrapper::singleton->Short_shortValue);
ret.push_back(val);
}
@@ -861,9 +865,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
int val = env->CallIntMethod(o, JavaClassWrapper::singleton->Integer_integerValue);
ret.push_back(val);
}
@@ -881,9 +885,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
int64_t val = env->CallLongMethod(o, JavaClassWrapper::singleton->Long_longValue);
ret.push_back(val);
}
@@ -901,9 +905,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
float val = env->CallFloatMethod(o, JavaClassWrapper::singleton->Float_floatValue);
ret.push_back(val);
}
@@ -921,9 +925,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
double val = env->CallDoubleMethod(o, JavaClassWrapper::singleton->Double_doubleValue);
ret.push_back(val);
}
@@ -942,9 +946,9 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
- if (!o)
+ if (!o) {
ret.push_back(Variant());
- else {
+ } else {
String val = jstring_to_string((jstring)o, env);
ret.push_back(val);
}
@@ -962,22 +966,19 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
}
Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
- if (class_cache.has(p_class))
+ if (class_cache.has(p_class)) {
return class_cache[p_class];
+ }
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, Ref<JavaClass>());
+ ERR_FAIL_NULL_V(env, Ref<JavaClass>());
jclass bclass = env->FindClass(p_class.utf8().get_data());
- ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
-
- //jmethodID getDeclaredMethods = env->GetMethodID(bclass,"getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
-
- //ERR_FAIL_COND_V(!getDeclaredMethods,Ref<JavaClass>());
+ ERR_FAIL_NULL_V(bclass, Ref<JavaClass>());
jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, getDeclaredMethods);
- ERR_FAIL_COND_V(!methods, Ref<JavaClass>());
+ ERR_FAIL_NULL_V(methods, Ref<JavaClass>());
Ref<JavaClass> java_class = memnew(JavaClass);
@@ -1057,9 +1058,10 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
float new_likeliness = 0;
float existing_likeliness = 0;
- if (E->get().param_types.size() != mi.param_types.size())
+ if (E->get().param_types.size() != mi.param_types.size()) {
continue;
- bool valid = true;
+ }
+ bool this_valid = true;
for (int j = 0; j < E->get().param_types.size(); j++) {
Variant::Type _new;
float new_l;
@@ -1068,15 +1070,16 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
JavaClass::_convert_to_variant_type(E->get().param_types[j], existing, existing_l);
JavaClass::_convert_to_variant_type(mi.param_types[j], _new, new_l);
if (_new != existing) {
- valid = false;
+ this_valid = false;
break;
}
new_likeliness += new_l;
existing_likeliness = existing_l;
}
- if (!valid)
+ if (!this_valid) {
continue;
+ }
if (new_likeliness > existing_likeliness) {
java_class->methods[str_method].erase(E);
@@ -1087,10 +1090,11 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
}
if (!discard) {
- if (mi._static)
+ if (mi._static) {
mi.method = env->GetStaticMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
- else
+ } else {
mi.method = env->GetMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
+ }
ERR_CONTINUE(!mi.method);
@@ -1100,7 +1104,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
env->DeleteLocalRef(obj);
env->DeleteLocalRef(param_types);
env->DeleteLocalRef(return_type);
- };
+ }
env->DeleteLocalRef(methods);
@@ -1151,10 +1155,10 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
singleton = this;
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
- jclass activityClass = env->FindClass("android/app/Activity");
- jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ jclass activity = env->FindClass("android/app/Activity");
+ jmethodID getClassLoader = env->GetMethodID(activity, "getClassLoader", "()Ljava/lang/ClassLoader;");
classLoader = env->CallObjectMethod(p_activity, getClassLoader);
classLoader = (jclass)env->NewGlobalRef(classLoader);
jclass classLoaderClass = env->FindClass("java/lang/ClassLoader");
@@ -1164,18 +1168,18 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;");
Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
- //
+
bclass = env->FindClass("java/lang/reflect/Method");
getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;");
getReturnType = env->GetMethodID(bclass, "getReturnType", "()Ljava/lang/Class;");
getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
getModifiers = env->GetMethodID(bclass, "getModifiers", "()I");
- ///
+
bclass = env->FindClass("java/lang/reflect/Field");
Field_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
Field_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I");
Field_get = env->GetMethodID(bclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
- // each
+
bclass = env->FindClass("java/lang/Boolean");
Boolean_booleanValue = env->GetMethodID(bclass, "booleanValue", "()Z");
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index 5cd2c382d2..b71c6ef1e6 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,10 @@
/*************************************************************************/
#include "java_godot_io_wrapper.h"
+
#include "core/error/error_list.h"
+#include "core/math/rect2.h"
+#include "core/variant/variant.h"
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
// we can't cache it.
@@ -50,10 +53,13 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I");
_get_cache_dir = p_env->GetMethodID(cls, "getCacheDir", "()Ljava/lang/String;");
_get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;");
+ _get_display_cutouts = p_env->GetMethodID(cls, "getDisplayCutouts", "()[I"),
+ _get_display_safe_area = p_env->GetMethodID(cls, "getDisplaySafeArea", "()[I"),
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
- _screen_get_usable_rect = p_env->GetMethodID(cls, "screenGetUsableRect", "()[I"),
+ _get_scaled_density = p_env->GetMethodID(cls, "getScaledDensity", "()F");
+ _get_screen_refresh_rate = p_env->GetMethodID(cls, "getScreenRefreshRate", "(D)D");
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
_show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V");
_hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V");
@@ -74,7 +80,7 @@ jobject GodotIOJavaWrapper::get_instance() {
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
if (_open_URI) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(env, ERR_UNAVAILABLE);
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
} else {
@@ -85,7 +91,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
String GodotIOJavaWrapper::get_cache_dir() {
if (_get_cache_dir) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_cache_dir);
return jstring_to_string(s, env);
} else {
@@ -96,7 +102,7 @@ String GodotIOJavaWrapper::get_cache_dir() {
String GodotIOJavaWrapper::get_user_data_dir() {
if (_get_data_dir) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
return jstring_to_string(s, env);
} else {
@@ -107,7 +113,7 @@ String GodotIOJavaWrapper::get_user_data_dir() {
String GodotIOJavaWrapper::get_locale() {
if (_get_locale) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
return jstring_to_string(s, env);
} else {
@@ -118,7 +124,7 @@ String GodotIOJavaWrapper::get_locale() {
String GodotIOJavaWrapper::get_model() {
if (_get_model) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
return jstring_to_string(s, env);
} else {
@@ -129,31 +135,74 @@ String GodotIOJavaWrapper::get_model() {
int GodotIOJavaWrapper::get_screen_dpi() {
if (_get_screen_DPI) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, 160);
+ ERR_FAIL_NULL_V(env, 160);
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
} else {
return 160;
}
}
-void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
- if (_screen_get_usable_rect) {
+float GodotIOJavaWrapper::get_scaled_density() {
+ if (_get_scaled_density) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, 1.0f);
+ return env->CallFloatMethod(godot_io_instance, _get_scaled_density);
+ } else {
+ return 1.0f;
+ }
+}
+
+float GodotIOJavaWrapper::get_screen_refresh_rate(float fallback) {
+ if (_get_screen_refresh_rate) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
- jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect);
- ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
- jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
- for (int i = 0; i < 4; i++) {
- p_rect_xywh[i] = arrayBody[i];
+ if (env == nullptr) {
+ ERR_PRINT("An error occurred while trying to get screen refresh rate.");
+ return fallback;
}
- env->ReleaseIntArrayElements(returnArray, arrayBody, 0);
+ return (float)env->CallDoubleMethod(godot_io_instance, _get_screen_refresh_rate, (double)fallback);
}
+ ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
+ return fallback;
+}
+
+Array GodotIOJavaWrapper::get_display_cutouts() {
+ Array result;
+ ERR_FAIL_NULL_V(_get_display_cutouts, result);
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, result);
+ jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _get_display_cutouts);
+ jint arrayLength = env->GetArrayLength(returnArray);
+ jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
+ int cutouts = arrayLength / 4;
+ for (int i = 0; i < cutouts; i++) {
+ int x = arrayBody[i * 4];
+ int y = arrayBody[i * 4 + 1];
+ int width = arrayBody[i * 4 + 2];
+ int height = arrayBody[i * 4 + 3];
+ Rect2 cutout(x, y, width, height);
+ result.append(cutout);
+ }
+ env->ReleaseIntArrayElements(returnArray, arrayBody, 0);
+ return result;
+}
+
+Rect2i GodotIOJavaWrapper::get_display_safe_area() {
+ Rect2i result;
+ ERR_FAIL_NULL_V(_get_display_safe_area, result);
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, result);
+ jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _get_display_safe_area);
+ ERR_FAIL_COND_V(env->GetArrayLength(returnArray) != 4, result);
+ jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
+ result = Rect2i(arrayBody[0], arrayBody[1], arrayBody[2], arrayBody[3]);
+ env->ReleaseIntArrayElements(returnArray, arrayBody, 0);
+ return result;
}
String GodotIOJavaWrapper::get_unique_id() {
if (_get_unique_id) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
return jstring_to_string(s, env);
} else {
@@ -162,13 +211,13 @@ String GodotIOJavaWrapper::get_unique_id() {
}
bool GodotIOJavaWrapper::has_vk() {
- return (_show_keyboard != 0) && (_hide_keyboard != 0);
+ return (_show_keyboard != nullptr) && (_hide_keyboard != nullptr);
}
void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (_show_keyboard) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
}
@@ -177,7 +226,7 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int
void GodotIOJavaWrapper::hide_vk() {
if (_hide_keyboard) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
}
}
@@ -185,7 +234,7 @@ void GodotIOJavaWrapper::hide_vk() {
void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
if (_set_screen_orientation) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
}
}
@@ -193,7 +242,7 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
int GodotIOJavaWrapper::get_screen_orientation() {
if (_get_screen_orientation) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, 0);
+ ERR_FAIL_NULL_V(env, 0);
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
} else {
return 0;
@@ -203,7 +252,7 @@ int GodotIOJavaWrapper::get_screen_orientation() {
String GodotIOJavaWrapper::get_system_dir(int p_dir, bool p_shared_storage) {
if (_get_system_dir) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String("."));
+ ERR_FAIL_NULL_V(env, String("."));
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir, p_shared_storage);
return jstring_to_string(s, env);
} else {
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 8f6d7f813f..aec7d1db97 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,8 @@
#include <android/log.h>
#include <jni.h>
+#include "core/math/rect2i.h"
+#include "core/variant/array.h"
#include "string_android.h"
// Class that makes functions in java/src/org/godotengine/godot/GodotIO.java callable from C++
@@ -48,10 +50,13 @@ private:
jmethodID _open_URI = 0;
jmethodID _get_cache_dir = 0;
jmethodID _get_data_dir = 0;
+ jmethodID _get_display_cutouts = 0;
+ jmethodID _get_display_safe_area = 0;
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
- jmethodID _screen_get_usable_rect = 0;
+ jmethodID _get_scaled_density = 0;
+ jmethodID _get_screen_refresh_rate = 0;
jmethodID _get_unique_id = 0;
jmethodID _show_keyboard = 0;
jmethodID _hide_keyboard = 0;
@@ -71,7 +76,10 @@ public:
String get_locale();
String get_model();
int get_screen_dpi();
- void screen_get_usable_rect(int (&p_rect_xywh)[4]);
+ float get_scaled_density();
+ float get_screen_refresh_rate(float fallback);
+ Array get_display_cutouts();
+ Rect2i get_display_safe_area();
String get_unique_id();
bool has_vk();
void show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end);
@@ -83,4 +91,4 @@ public:
String get_system_dir(int p_dir, bool p_shared_storage);
};
-#endif /* !JAVA_GODOT_IO_WRAPPER_H */
+#endif // JAVA_GODOT_IO_WRAPPER_H
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 3236512f5c..de666f1b11 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,6 +49,7 @@
#include "os_android.h"
#include "string_android.h"
#include "thread_jandroid.h"
+#include "tts_android.h"
#include <android/input.h>
#include <unistd.h>
@@ -61,7 +62,6 @@ static AndroidInputHandler *input_handler = nullptr;
static GodotJavaWrapper *godot_java = nullptr;
static GodotIOJavaWrapper *godot_io_java = nullptr;
-static bool initialized = false;
static SafeNumeric<int> step; // Shared between UI and render threads
static Size2 new_size;
@@ -79,8 +79,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject godot_instance, jobject p_asset_manager, jboolean p_use_apk_expansion) {
- initialized = true;
-
JavaVM *jvm;
env->GetJavaVM(&jvm);
@@ -96,17 +94,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
DirAccessJAndroid::setup(godot_io_java->get_instance());
NetSocketAndroid::setup(godot_java->get_member_object("netUtils", "Lorg/godotengine/godot/utils/GodotNetUtils;", env));
+ TTS_Android::setup(godot_java->get_member_object("tts", "Lorg/godotengine/godot/tts/GodotTTS;", env));
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
- char wd[500];
- getcwd(wd, 500);
-
godot_java->on_video_init(env);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
// lets cleanup
+ if (java_class_wrapper) {
+ memdelete(java_class_wrapper);
+ }
if (godot_io_java) {
delete godot_io_java;
}
@@ -117,6 +116,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
delete input_handler;
}
if (os_android) {
+ os_android->main_loop_end();
delete os_android;
}
}
@@ -138,7 +138,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
for (int i = 0; i < cmdlen; i++) {
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
- const char *rawString = env->GetStringUTFChars(string, 0);
+ const char *rawString = env->GetStringUTFChars(string, nullptr);
cmdline[i] = rawString;
j_cmdline[i] = string;
@@ -146,7 +146,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
}
}
- Error err = Main::setup("apk", cmdlen, (char **)cmdline, false);
+ Error err = Main::setup(OS_Android::ANDROID_EXEC_PATH, cmdlen, (char **)cmdline, false);
if (cmdline) {
if (j_cmdline) {
for (int i = 0; i < cmdlen; ++i) {
@@ -200,17 +200,23 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz) {
- if (step.get() == 0)
+ if (step.get() == 0) {
return;
+ }
if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) {
dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST);
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
- if (step.get() == -1)
- return;
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos) {
+ TTS_Android::_java_utterance_callback(event, id, pos);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
+ if (step.get() == -1) {
+ return true;
+ }
if (step.get() == 0) {
// Since Godot is initialized on the UI thread, main_thread_id was set to that thread's id,
@@ -218,12 +224,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
Main::setup2(Thread::get_caller_id());
input_handler = new AndroidInputHandler();
step.increment();
- return;
+ return true;
}
if (step.get() == 1) {
if (!Main::start()) {
- return; // should exit instead and print the error
+ return true; // should exit instead and print the error
}
godot_java->on_godot_setup_completed(env);
@@ -237,14 +243,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
DisplayServerAndroid::get_singleton()->process_magnetometer(magnetometer);
DisplayServerAndroid::get_singleton()->process_gyroscope(gyroscope);
- if (os_android->main_loop_iterate()) {
+ bool should_swap_buffers = false;
+ if (os_android->main_loop_iterate(&should_swap_buffers)) {
godot_java->force_quit(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) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
Vector<AndroidInputHandler::TouchPos> points;
for (int i = 0; i < pointer_count; i++) {
@@ -279,32 +289,36 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNI
// 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) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
input_handler->process_hover(p_type, Point2(p_x, p_y));
}
// 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) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
input_handler->process_scroll(Point2(p_x, p_y));
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
@@ -369,8 +383,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
input_handler->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
}
@@ -392,15 +407,17 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
os_android->main_loop_focusin();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
os_android->main_loop_focusout();
}
@@ -413,7 +430,7 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) {
Object *obj = ObjectDB::get_instance(ObjectID(ID));
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
int res = env->PushLocalFrame(16);
ERR_FAIL_COND(res != 0);
@@ -424,26 +441,26 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
Variant *vlist = (Variant *)alloca(sizeof(Variant) * count);
Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count);
for (int i = 0; i < count; i++) {
- jobject obj = env->GetObjectArrayElement(params, i);
+ jobject jobj = env->GetObjectArrayElement(params, i);
Variant v;
- if (obj)
- v = _jobject_to_variant(env, obj);
+ if (jobj) {
+ v = _jobject_to_variant(env, jobj);
+ }
memnew_placement(&vlist[i], Variant);
vlist[i] = v;
vptr[i] = &vlist[i];
- env->DeleteLocalRef(obj);
- };
+ env->DeleteLocalRef(jobj);
+ }
Callable::CallError err;
- obj->call(str_method, (const Variant **)vptr, count, err);
- // something
+ obj->callp(str_method, (const Variant **)vptr, count, err);
env->PopLocalFrame(nullptr);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) {
Object *obj = ObjectDB::get_instance(ObjectID(ID));
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
int res = env->PushLocalFrame(16);
ERR_FAIL_COND(res != 0);
@@ -451,18 +468,21 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
String str_method = jstring_to_string(method, env);
int count = env->GetArrayLength(params);
- Variant args[VARIANT_ARG_MAX];
-
- for (int i = 0; i < MIN(count, VARIANT_ARG_MAX); i++) {
- jobject obj = env->GetObjectArrayElement(params, i);
- if (obj)
- args[i] = _jobject_to_variant(env, obj);
- env->DeleteLocalRef(obj);
- };
-
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
- // something
+
+ Variant *args = (Variant *)alloca(sizeof(Variant) * count);
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * count);
+
+ for (int i = 0; i < count; i++) {
+ jobject jobj = env->GetObjectArrayElement(params, i);
+ if (jobj) {
+ args[i] = _jobject_to_variant(env, jobj);
+ }
+ env->DeleteLocalRef(jobj);
+ argptrs[i] = &args[i];
+ }
+
+ MessageQueue::get_singleton()->push_callp(obj, str_method, (const Variant **)argptrs, count);
+
env->PopLocalFrame(nullptr);
}
@@ -478,17 +498,21 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
+ // We force redraw to ensure we render at least once when resuming the app.
+ Main::force_redraw();
if (os_android->get_main_loop()) {
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
}
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
if (os_android->get_main_loop()) {
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 7ea74480cb..e1d30eea77 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,8 @@ 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 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 void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
+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);
@@ -71,4 +72,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNI
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
}
-#endif /* !JAVA_GODOT_LIB_JNI_H */
+#endif // JAVA_GODOT_LIB_JNI_H
diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp
index 837d2aeced..0153ba96fc 100644
--- a/platform/android/java_godot_view_wrapper.cpp
+++ b/platform/android/java_godot_view_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
_godot_view = env->NewGlobalRef(godot_view);
@@ -48,27 +48,27 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
}
void GodotJavaViewWrapper::request_pointer_capture() {
- if (_request_pointer_capture != 0) {
+ if (_request_pointer_capture != nullptr) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(_godot_view, _request_pointer_capture);
}
}
void GodotJavaViewWrapper::release_pointer_capture() {
- if (_request_pointer_capture != 0) {
+ if (_request_pointer_capture != nullptr) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(_godot_view, _release_pointer_capture);
}
}
void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) {
- if (_set_pointer_icon != 0) {
+ if (_set_pointer_icon != nullptr) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(_godot_view, _set_pointer_icon, pointer_type);
}
@@ -76,7 +76,7 @@ void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) {
GodotJavaViewWrapper::~GodotJavaViewWrapper() {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
+ ERR_FAIL_NULL(env);
env->DeleteGlobalRef(_godot_view);
env->DeleteGlobalRef(_cls);
diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h
index da547d8118..b1f258bbb5 100644
--- a/platform/android/java_godot_view_wrapper.h
+++ b/platform/android/java_godot_view_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,4 +57,4 @@ public:
~GodotJavaViewWrapper();
};
-#endif //GODOT_JAVA_GODOT_VIEW_WRAPPER_H
+#endif // GODOT_JAVA_GODOT_VIEW_WRAPPER_H
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index bfd93345f3..e3456fe4e4 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
+ _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
@@ -76,6 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
+ _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
@@ -91,11 +93,10 @@ jobject GodotJavaWrapper::get_activity() {
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
if (godot_class) {
- if (p_env == nullptr)
+ if (p_env == nullptr) {
p_env = get_jni_env();
-
- ERR_FAIL_COND_V(p_env == nullptr, nullptr);
-
+ }
+ ERR_FAIL_NULL_V(p_env, nullptr);
jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
return p_env->GetStaticObjectField(godot_class, fid);
} else {
@@ -106,8 +107,7 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
jobject GodotJavaWrapper::get_class_loader() {
if (_get_class_loader) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, nullptr);
-
+ ERR_FAIL_NULL_V(env, nullptr);
return env->CallObjectMethod(activity, _get_class_loader);
} else {
return nullptr;
@@ -119,8 +119,7 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
return _godot_view;
}
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, nullptr);
-
+ ERR_FAIL_NULL_V(env, nullptr);
jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
_godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
return _godot_view;
@@ -128,10 +127,10 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init) {
- if (p_env == nullptr)
+ if (p_env == nullptr) {
p_env = get_jni_env();
- ERR_FAIL_COND(p_env == nullptr);
-
+ }
+ ERR_FAIL_NULL(p_env);
p_env->CallVoidMethod(godot_instance, _on_video_init);
}
}
@@ -150,27 +149,27 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
- ERR_FAIL_COND(p_env == nullptr);
+ ERR_FAIL_NULL(p_env);
p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
}
void GodotJavaWrapper::restart(JNIEnv *p_env) {
if (_restart) {
- if (p_env == nullptr)
+ if (p_env == nullptr) {
p_env = get_jni_env();
- ERR_FAIL_COND(p_env == nullptr);
-
+ }
+ ERR_FAIL_NULL(p_env);
p_env->CallVoidMethod(godot_instance, _restart);
}
}
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
if (_finish) {
- if (p_env == nullptr)
+ if (p_env == nullptr) {
p_env = get_jni_env();
- ERR_FAIL_COND(p_env == nullptr);
-
+ }
+ ERR_FAIL_NULL(p_env);
p_env->CallVoidMethod(godot_instance, _finish);
}
}
@@ -178,8 +177,7 @@ void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
if (_set_keep_screen_on) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
-
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
}
}
@@ -187,8 +185,7 @@ void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
if (_alert) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
-
+ ERR_FAIL_NULL(env);
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
@@ -197,24 +194,21 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
int GodotJavaWrapper::get_gles_version_code() {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, 0);
-
+ ERR_FAIL_NULL_V(env, 0);
if (_get_GLES_version_code) {
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
}
-
return 0;
}
bool GodotJavaWrapper::has_get_clipboard() {
- return _get_clipboard != 0;
+ return _get_clipboard != nullptr;
}
String GodotJavaWrapper::get_clipboard() {
if (_get_clipboard) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
-
+ ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
return jstring_to_string(s, env);
} else {
@@ -225,8 +219,7 @@ String GodotJavaWrapper::get_clipboard() {
String GodotJavaWrapper::get_input_fallback_mapping() {
if (_get_input_fallback_mapping) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, String());
-
+ ERR_FAIL_NULL_V(env, String());
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
return jstring_to_string(fallback_mapping, env);
} else {
@@ -235,24 +228,36 @@ String GodotJavaWrapper::get_input_fallback_mapping() {
}
bool GodotJavaWrapper::has_set_clipboard() {
- return _set_clipboard != 0;
+ return _set_clipboard != nullptr;
}
void GodotJavaWrapper::set_clipboard(const String &p_text) {
if (_set_clipboard) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
-
+ ERR_FAIL_NULL(env);
jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
}
}
+bool GodotJavaWrapper::has_has_clipboard() {
+ return _has_clipboard != nullptr;
+}
+
+bool GodotJavaWrapper::has_clipboard() {
+ if (_has_clipboard) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, false);
+ return env->CallBooleanMethod(godot_instance, _has_clipboard);
+ } else {
+ return false;
+ }
+}
+
bool GodotJavaWrapper::request_permission(const String &p_name) {
if (_request_permission) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, false);
-
+ ERR_FAIL_NULL_V(env, false);
jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
} else {
@@ -263,8 +268,7 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
bool GodotJavaWrapper::request_permissions() {
if (_request_permissions) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, false);
-
+ ERR_FAIL_NULL_V(env, false);
return env->CallBooleanMethod(godot_instance, _request_permissions);
} else {
return false;
@@ -275,14 +279,12 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
Vector<String> permissions_list;
if (_get_granted_permissions) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, permissions_list);
-
+ ERR_FAIL_NULL_V(env, permissions_list);
jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
- int i = 0;
jsize len = env->GetArrayLength(*arr);
- for (i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++) {
jstring jstr = (jstring)env->GetObjectArrayElement(*arr, i);
String str = jstring_to_string(jstr, env);
permissions_list.push_back(str);
@@ -295,8 +297,7 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
-
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(godot_instance, _init_input_devices);
}
}
@@ -304,8 +305,7 @@ void GodotJavaWrapper::init_input_devices() {
jobject GodotJavaWrapper::get_surface() {
if (_get_surface) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, nullptr);
-
+ ERR_FAIL_NULL_V(env, nullptr);
return env->CallObjectMethod(godot_instance, _get_surface);
} else {
return nullptr;
@@ -315,8 +315,7 @@ jobject GodotJavaWrapper::get_surface() {
bool GodotJavaWrapper::is_activity_resumed() {
if (_is_activity_resumed) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND_V(env == nullptr, false);
-
+ ERR_FAIL_NULL_V(env, false);
return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
} else {
return false;
@@ -326,8 +325,19 @@ bool GodotJavaWrapper::is_activity_resumed() {
void GodotJavaWrapper::vibrate(int p_duration_ms) {
if (_vibrate) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_COND(env == nullptr);
-
+ ERR_FAIL_NULL(env);
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
+
+void GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+ if (_create_new_godot_instance) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL(env);
+ jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ for (int i = 0; i < args.size(); i++) {
+ env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data()));
+ }
+ env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs);
+ }
+}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 0e20747a16..bbf7c0ae33 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,7 @@
#include <android/log.h>
#include <jni.h>
+#include "core/templates/list.h"
#include "java_godot_view_wrapper.h"
#include "string_android.h"
@@ -50,25 +51,27 @@ private:
GodotJavaViewWrapper *_godot_view = nullptr;
- jmethodID _on_video_init = 0;
- jmethodID _restart = 0;
- jmethodID _finish = 0;
- jmethodID _set_keep_screen_on = 0;
- jmethodID _alert = 0;
- jmethodID _get_GLES_version_code = 0;
- jmethodID _get_clipboard = 0;
- jmethodID _set_clipboard = 0;
- jmethodID _request_permission = 0;
- jmethodID _request_permissions = 0;
- jmethodID _get_granted_permissions = 0;
- jmethodID _init_input_devices = 0;
- jmethodID _get_surface = 0;
- jmethodID _is_activity_resumed = 0;
- jmethodID _vibrate = 0;
- jmethodID _get_input_fallback_mapping = 0;
- jmethodID _on_godot_setup_completed = 0;
- jmethodID _on_godot_main_loop_started = 0;
- jmethodID _get_class_loader = 0;
+ jmethodID _on_video_init = nullptr;
+ jmethodID _restart = nullptr;
+ jmethodID _finish = nullptr;
+ jmethodID _set_keep_screen_on = nullptr;
+ jmethodID _alert = nullptr;
+ jmethodID _get_GLES_version_code = nullptr;
+ jmethodID _get_clipboard = nullptr;
+ jmethodID _set_clipboard = nullptr;
+ jmethodID _has_clipboard = nullptr;
+ jmethodID _request_permission = nullptr;
+ jmethodID _request_permissions = nullptr;
+ jmethodID _get_granted_permissions = nullptr;
+ jmethodID _init_input_devices = nullptr;
+ jmethodID _get_surface = nullptr;
+ jmethodID _is_activity_resumed = nullptr;
+ jmethodID _vibrate = nullptr;
+ jmethodID _get_input_fallback_mapping = nullptr;
+ jmethodID _on_godot_setup_completed = nullptr;
+ jmethodID _on_godot_main_loop_started = nullptr;
+ jmethodID _get_class_loader = nullptr;
+ jmethodID _create_new_godot_instance = nullptr;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -92,6 +95,8 @@ public:
String get_clipboard();
bool has_set_clipboard();
void set_clipboard(const String &p_text);
+ bool has_has_clipboard();
+ bool has_clipboard();
bool request_permission(const String &p_name);
bool request_permissions();
Vector<String> get_granted_permissions() const;
@@ -100,6 +105,7 @@ public:
bool is_activity_resumed();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
+ void create_new_godot_instance(List<String> args);
};
-#endif /* !JAVA_GODOT_WRAPPER_H */
+#endif // JAVA_GODOT_WRAPPER_H
diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp
index 94652ab7ac..193ef61264 100644
--- a/platform/android/jni_utils.cpp
+++ b/platform/android/jni_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,7 +46,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
env->DeleteLocalRef(bclass);
} else {
v.val.z = *p_arg;
- };
+ }
} break;
case Variant::INT: {
if (force_jobject) {
@@ -61,7 +61,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
} else {
v.val.i = *p_arg;
- };
+ }
} break;
case Variant::FLOAT: {
if (force_jobject) {
@@ -76,7 +76,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
} else {
v.val.f = *p_arg;
- };
+ }
} break;
case Variant::STRING: {
String s = *p_arg;
@@ -111,7 +111,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data());
env->SetObjectArrayElement(jkeys, j, str);
env->DeleteLocalRef(str);
- };
+ }
jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V");
jvalue val;
@@ -123,12 +123,12 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
for (int j = 0; j < keys.size(); j++) {
Variant var = dict[keys[j]];
- jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true);
- env->SetObjectArrayElement(jvalues, j, v.val.l);
- if (v.obj) {
- env->DeleteLocalRef(v.obj);
+ jvalret valret = _variant_to_jvalue(env, var.get_type(), &var, true);
+ env->SetObjectArrayElement(jvalues, j, valret.val.l);
+ if (valret.obj) {
+ env->DeleteLocalRef(valret.obj);
}
- };
+ }
jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V");
val.l = jvalues;
@@ -186,7 +186,7 @@ String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
if (array) {
jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z");
jboolean isarr = env->CallBooleanMethod(cls, isArray);
- (*array) = isarr ? true : false;
+ (*array) = isarr != 0;
}
String name = jstring_to_string(clsName, env);
env->DeleteLocalRef(clsName);
@@ -205,7 +205,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
if (name == "java.lang.String") {
return jstring_to_string((jstring)obj, env);
- };
+ }
if (name == "[Ljava.lang.String;") {
jobjectArray arr = (jobjectArray)obj;
@@ -219,20 +219,20 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
}
return sarr;
- };
+ }
if (name == "java.lang.Boolean") {
jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z");
bool ret = env->CallBooleanMethod(obj, boolValue);
return ret;
- };
+ }
if (name == "java.lang.Integer" || name == "java.lang.Long") {
jclass nclass = env->FindClass("java/lang/Number");
jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
jlong ret = env->CallLongMethod(obj, longValue);
return ret;
- };
+ }
if (name == "[I") {
jintArray arr = (jintArray)obj;
@@ -243,7 +243,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
int *w = sarr.ptrw();
env->GetIntArrayRegion(arr, 0, fCount, w);
return sarr;
- };
+ }
if (name == "[B") {
jbyteArray arr = (jbyteArray)obj;
@@ -254,14 +254,14 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
uint8_t *w = sarr.ptrw();
env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast<signed char *>(w));
return sarr;
- };
+ }
if (name == "java.lang.Float" || name == "java.lang.Double") {
jclass nclass = env->FindClass("java/lang/Number");
jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
double ret = env->CallDoubleMethod(obj, doubleValue);
return ret;
- };
+ }
if (name == "[D") {
jdoubleArray arr = (jdoubleArray)obj;
@@ -275,9 +275,9 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
double n;
env->GetDoubleArrayRegion(arr, i, 1, &n);
w[i] = n;
- };
+ }
return sarr;
- };
+ }
if (name == "[F") {
jfloatArray arr = (jfloatArray)obj;
@@ -291,9 +291,9 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
float n;
env->GetFloatArrayRegion(arr, i, 1, &n);
w[i] = n;
- };
+ }
return sarr;
- };
+ }
if (name == "[Ljava.lang.Object;") {
jobjectArray arr = (jobjectArray)obj;
@@ -308,7 +308,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
}
return varr;
- };
+ }
if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") {
Dictionary ret;
@@ -327,10 +327,10 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
for (int i = 0; i < keys.size(); i++) {
ret[keys[i]] = vals[i];
- };
+ }
return ret;
- };
+ }
env->DeleteLocalRef(c);
@@ -359,8 +359,9 @@ Variant::Type get_jni_type(const String &p_type) {
int idx = 0;
while (_type_to_vtype[idx].name) {
- if (p_type == _type_to_vtype[idx].name)
+ if (p_type == _type_to_vtype[idx].name) {
return _type_to_vtype[idx].type;
+ }
idx++;
}
@@ -390,8 +391,9 @@ const char *get_jni_sig(const String &p_type) {
int idx = 0;
while (_type_to_vtype[idx].name) {
- if (p_type == _type_to_vtype[idx].name)
+ if (p_type == _type_to_vtype[idx].name) {
return _type_to_vtype[idx].sig;
+ }
idx++;
}
diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h
index f2b89392b5..7d5da29a65 100644
--- a/platform/android/jni_utils.h
+++ b/platform/android/jni_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp
index dbd1870ee6..225a1132fe 100644
--- a/platform/android/net_socket_android.cpp
+++ b/platform/android/net_socket_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,10 +32,10 @@
#include "thread_jandroid.h"
-jobject NetSocketAndroid::net_utils = 0;
-jclass NetSocketAndroid::cls = 0;
-jmethodID NetSocketAndroid::_multicast_lock_acquire = 0;
-jmethodID NetSocketAndroid::_multicast_lock_release = 0;
+jobject NetSocketAndroid::net_utils = nullptr;
+jclass NetSocketAndroid::cls = nullptr;
+jmethodID NetSocketAndroid::_multicast_lock_acquire = nullptr;
+jmethodID NetSocketAndroid::_multicast_lock_release = nullptr;
void NetSocketAndroid::setup(jobject p_net_utils) {
JNIEnv *env = get_jni_env();
@@ -77,18 +77,21 @@ NetSocketAndroid::~NetSocketAndroid() {
void NetSocketAndroid::close() {
NetSocketPosix::close();
- if (wants_broadcast)
+ if (wants_broadcast) {
multicast_lock_release();
- if (multicast_groups)
+ }
+ if (multicast_groups) {
multicast_lock_release();
+ }
wants_broadcast = false;
multicast_groups = 0;
}
Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) {
Error err = NetSocketPosix::set_broadcasting_enabled(p_enabled);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
if (p_enabled != wants_broadcast) {
if (p_enabled) {
@@ -105,11 +108,13 @@ Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) {
Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::join_multicast_group(p_multi_address, p_if_name);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
- if (!multicast_groups)
+ if (!multicast_groups) {
multicast_lock_acquire();
+ }
multicast_groups++;
return OK;
@@ -117,14 +122,16 @@ Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, S
Error NetSocketAndroid::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::leave_multicast_group(p_multi_address, p_if_name);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
ERR_FAIL_COND_V(multicast_groups == 0, ERR_BUG);
multicast_groups--;
- if (!multicast_groups)
+ if (!multicast_groups) {
multicast_lock_release();
+ }
return OK;
}
diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h
index 60090c26bb..97a611cb04 100644
--- a/platform/android/net_socket_android.h
+++ b/platform/android/net_socket_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -74,4 +74,4 @@ public:
~NetSocketAndroid();
};
-#endif
+#endif // NET_SOCKET_ANDROID_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index ffd69a56b9..6674428de8 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,6 +35,8 @@
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
#include "platform/android/display_server_android.h"
+#include "scene/main/scene_tree.h"
+#include "servers/rendering_server.h"
#include "dir_access_jandroid.h"
#include "file_access_android.h"
@@ -45,6 +47,8 @@
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
+const char *OS_Android::ANDROID_EXEC_PATH = "apk";
+
String _remove_symlink(const String &dir) {
// Workaround for Android 6.0+ using a symlink.
// Save the current directory.
@@ -72,26 +76,34 @@ public:
};
void OS_Android::alert(const String &p_alert, const String &p_title) {
- GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
- ERR_FAIL_COND(!godot_java);
-
+ ERR_FAIL_NULL(godot_java);
godot_java->alert(p_alert, p_title);
}
void OS_Android::initialize_core() {
OS_Unix::initialize_core();
- if (use_apk_expansion)
+#ifdef TOOLS_ENABLED
+ FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
+#else
+ if (use_apk_expansion) {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
- else {
+ } else {
FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES);
}
+#endif
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
- if (use_apk_expansion)
+
+#ifdef TOOLS_ENABLED
+ DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
+#else
+ if (use_apk_expansion) {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
- else
+ } else {
DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES);
+ }
+#endif
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
@@ -124,7 +136,7 @@ void OS_Android::finalize() {
}
OS_Android *OS_Android::get_singleton() {
- return (OS_Android *)OS::get_singleton();
+ return static_cast<OS_Android *>(OS::get_singleton());
}
GodotJavaWrapper *OS_Android::get_godot_java() {
@@ -147,9 +159,14 @@ Vector<String> OS_Android::get_granted_permissions() const {
return godot_java->get_granted_permissions();
}
-Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
p_library_handle = dlopen(p_path.utf8().get_data(), RTLD_NOW);
- ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
+ ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
+
+ if (r_resolved_path != nullptr) {
+ *r_resolved_path = p_path;
+ }
+
return OK;
}
@@ -162,20 +179,34 @@ MainLoop *OS_Android::get_main_loop() const {
}
void OS_Android::main_loop_begin() {
- if (main_loop)
+ if (main_loop) {
main_loop->initialize();
+ }
}
-bool OS_Android::main_loop_iterate() {
- if (!main_loop)
+bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) {
+ if (!main_loop) {
return false;
+ }
DisplayServerAndroid::get_singleton()->process_events();
- return Main::iteration();
+ uint64_t current_frames_drawn = Engine::get_singleton()->get_frames_drawn();
+ bool exit = Main::iteration();
+
+ if (r_should_swap_buffers) {
+ *r_should_swap_buffers = !is_in_low_processor_usage_mode() || RenderingServer::get_singleton()->has_changed() || current_frames_drawn != Engine::get_singleton()->get_frames_drawn();
+ }
+
+ return exit;
}
void OS_Android::main_loop_end() {
- if (main_loop)
+ if (main_loop) {
+ SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
+ if (scene_tree) {
+ scene_tree->quit();
+ }
main_loop->finalize();
+ }
}
void OS_Android::main_loop_focusout() {
@@ -193,7 +224,11 @@ Error OS_Android::shell_open(String p_uri) {
}
String OS_Android::get_resource_dir() const {
+#ifdef TOOLS_ENABLED
+ return OS_Unix::get_resource_dir();
+#else
return "/"; //android has its own filesystem for resources inside the APK
+#endif
}
String OS_Android::get_locale() const {
@@ -207,8 +242,9 @@ String OS_Android::get_locale() const {
String OS_Android::get_model_name() const {
String model = godot_io_java->get_model();
- if (!model.is_empty())
+ if (!model.is_empty()) {
return model;
+ }
return OS_Unix::get_model_name();
}
@@ -217,9 +253,18 @@ String OS_Android::get_data_path() const {
return get_user_data_dir();
}
+String OS_Android::get_executable_path() const {
+ // Since unix process creation is restricted on Android, we bypass
+ // OS_Unix::get_executable_path() so we can return ANDROID_EXEC_PATH.
+ // Detection of ANDROID_EXEC_PATH allows to handle process creation in an Android compliant
+ // manner.
+ return OS::get_executable_path();
+}
+
String OS_Android::get_user_data_dir() const {
- if (!data_dir_cache.is_empty())
+ if (!data_dir_cache.is_empty()) {
return data_dir_cache;
+ }
String data_dir = godot_io_java->get_user_data_dir();
if (!data_dir.is_empty()) {
@@ -230,8 +275,9 @@ String OS_Android::get_user_data_dir() const {
}
String OS_Android::get_cache_path() const {
- if (!cache_dir_cache.is_empty())
+ if (!cache_dir_cache.is_empty()) {
return cache_dir_cache;
+ }
String cache_dir = godot_io_java->get_cache_dir();
if (!cache_dir.is_empty()) {
@@ -243,8 +289,9 @@ String OS_Android::get_cache_path() const {
String OS_Android::get_unique_id() const {
String unique_id = godot_io_java->get_unique_id();
- if (!unique_id.is_empty())
+ if (!unique_id.is_empty()) {
return unique_id;
+ }
return OS::get_unique_id();
}
@@ -263,7 +310,7 @@ Size2i OS_Android::get_display_size() const {
void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
#if defined(GLES3_ENABLED)
- ERR_FAIL_COND(!p_gl_extensions);
+ ERR_FAIL_NULL(p_gl_extensions);
gl_extensions = p_gl_extensions;
#endif
}
@@ -286,6 +333,10 @@ void OS_Android::vibrate_handheld(int p_duration_ms) {
godot_java->vibrate(p_duration_ms);
}
+String OS_Android::get_config_path() const {
+ return get_user_data_dir().plus_file("config");
+}
+
bool OS_Android::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "mobile") {
return true;
@@ -335,5 +386,26 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
DisplayServerAndroid::register_android_driver();
}
+Error OS_Android::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments);
+ } else {
+ return OS_Unix::execute(p_path, p_arguments, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, p_open_console);
+ }
+}
+
+Error OS_Android::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments, r_child_id);
+ } else {
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
+ }
+}
+
+Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
+ godot_java->create_new_godot_instance(p_arguments);
+ return OK;
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index a62f79952c..3f607eac48 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,7 +52,7 @@ private:
#endif
#if defined(VULKAN_ENABLED)
- ANativeWindow *native_window;
+ ANativeWindow *native_window = nullptr;
#endif
mutable String data_dir_cache;
@@ -60,12 +60,14 @@ private:
AudioDriverOpenSL audio_driver_android;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
- GodotJavaWrapper *godot_java;
- GodotIOJavaWrapper *godot_io_java;
+ GodotJavaWrapper *godot_java = nullptr;
+ GodotIOJavaWrapper *godot_io_java = nullptr;
public:
+ static const char *ANDROID_EXEC_PATH;
+
virtual void initialize_core() override;
virtual void initialize() override;
@@ -88,13 +90,13 @@ public:
virtual void alert(const String &p_alert, const String &p_title) override;
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
virtual String get_name() const override;
virtual MainLoop *get_main_loop() const override;
void main_loop_begin();
- bool main_loop_iterate();
+ bool main_loop_iterate(bool *r_should_swap_buffers = nullptr);
void main_loop_end();
void main_loop_focusout();
void main_loop_focusin();
@@ -108,6 +110,7 @@ public:
ANativeWindow *get_native_window() const;
virtual Error shell_open(String p_uri) override;
+ virtual String get_executable_path() const override;
virtual String get_user_data_dir() const override;
virtual String get_data_path() const override;
virtual String get_cache_path() const override;
@@ -121,9 +124,15 @@ public:
void vibrate_handheld(int p_duration_ms) override;
+ virtual String get_config_path() const override;
+
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
+ virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+
virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
~OS_Android();
};
-#endif
+#endif // OS_ANDROID_H
diff --git a/platform/android/platform_config.h b/platform/android/platform_config.h
index 601db9951f..40bee40180 100644
--- a/platform/android/platform_config.h
+++ b/platform/android/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 6a20d5eafc..7a39e2003d 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -114,19 +114,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS
String signal_name = jstring_to_string(j_signal_name, env);
int count = env->GetArrayLength(j_signal_params);
- ERR_FAIL_COND_MSG(count > VARIANT_ARG_MAX, "Maximum argument count exceeded!");
- Variant variant_params[VARIANT_ARG_MAX];
- const Variant *args[VARIANT_ARG_MAX];
+ Variant *variant_params = (Variant *)alloca(sizeof(Variant) * count);
+ const Variant **args = (const Variant **)alloca(sizeof(Variant *) * count);
for (int i = 0; i < count; i++) {
jobject j_param = env->GetObjectArrayElement(j_signal_params, i);
variant_params[i] = _jobject_to_variant(env, j_param);
args[i] = &variant_params[i];
env->DeleteLocalRef(j_param);
- };
+ }
- singleton->emit_signal(SNAME(signal_name), args, count);
+ singleton->emit_signalp(StringName(signal_name), args, count);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths) {
diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h
index b87f922e03..35f9d5b513 100644
--- a/platform/android/plugin/godot_plugin_jni.h
+++ b/platform/android/plugin/godot_plugin_jni.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/string_android.h b/platform/android/string_android.h
index 3721315d3f..79c71b5d04 100644
--- a/platform/android/string_android.h
+++ b/platform/android/string_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#ifndef STRING_ANDROID_H
#define STRING_ANDROID_H
+
#include "core/string/ustring.h"
#include "thread_jandroid.h"
#include <jni.h>
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index ba379c8d43..61b471866f 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h
index ff13ae911f..3b000517fd 100644
--- a/platform/android/thread_jandroid.h
+++ b/platform/android/thread_jandroid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,4 +38,4 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env);
void setup_android_thread();
JNIEnv *get_jni_env();
-#endif
+#endif // THREAD_JANDROID_H
diff --git a/platform/android/tts_android.cpp b/platform/android/tts_android.cpp
new file mode 100644
index 0000000000..27ba8da448
--- /dev/null
+++ b/platform/android/tts_android.cpp
@@ -0,0 +1,189 @@
+/*************************************************************************/
+/* tts_android.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "tts_android.h"
+
+#include "java_godot_wrapper.h"
+#include "os_android.h"
+#include "string_android.h"
+#include "thread_jandroid.h"
+
+jobject TTS_Android::tts = nullptr;
+jclass TTS_Android::cls = nullptr;
+
+jmethodID TTS_Android::_is_speaking = nullptr;
+jmethodID TTS_Android::_is_paused = nullptr;
+jmethodID TTS_Android::_get_voices = nullptr;
+jmethodID TTS_Android::_speak = nullptr;
+jmethodID TTS_Android::_pause_speaking = nullptr;
+jmethodID TTS_Android::_resume_speaking = nullptr;
+jmethodID TTS_Android::_stop_speaking = nullptr;
+
+HashMap<int, Char16String> TTS_Android::ids;
+
+void TTS_Android::setup(jobject p_tts) {
+ JNIEnv *env = get_jni_env();
+
+ tts = env->NewGlobalRef(p_tts);
+
+ jclass c = env->GetObjectClass(tts);
+ cls = (jclass)env->NewGlobalRef(c);
+
+ _is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
+ _is_paused = env->GetMethodID(cls, "isPaused", "()Z");
+ _get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
+ _speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
+ _pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
+ _resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
+ _stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
+}
+
+void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
+ if (ids.has(p_id)) {
+ int pos = 0;
+ if ((DisplayServer::TTSUtteranceEvent)p_event == DisplayServer::TTS_UTTERANCE_BOUNDARY) {
+ // Convert position from UTF-16 to UTF-32.
+ const Char16String &string = ids[p_id];
+ for (int i = 0; i < MIN(p_pos, string.length()); i++) {
+ char16_t c = string[i];
+ if ((c & 0xfffffc00) == 0xd800) {
+ i++;
+ }
+ pos++;
+ }
+ } else if ((DisplayServer::TTSUtteranceEvent)p_event != DisplayServer::TTS_UTTERANCE_STARTED) {
+ ids.erase(p_id);
+ }
+ DisplayServer::get_singleton()->tts_post_utterance_event((DisplayServer::TTSUtteranceEvent)p_event, p_id, pos);
+ }
+}
+
+bool TTS_Android::is_speaking() {
+ if (_is_speaking) {
+ JNIEnv *env = get_jni_env();
+
+ ERR_FAIL_COND_V(env == nullptr, false);
+ return env->CallBooleanMethod(tts, _is_speaking);
+ } else {
+ return false;
+ }
+}
+
+bool TTS_Android::is_paused() {
+ if (_is_paused) {
+ JNIEnv *env = get_jni_env();
+
+ ERR_FAIL_COND_V(env == nullptr, false);
+ return env->CallBooleanMethod(tts, _is_paused);
+ } else {
+ return false;
+ }
+}
+
+Array TTS_Android::get_voices() {
+ Array list;
+ if (_get_voices) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, list);
+
+ jobject voices_object = env->CallObjectMethod(tts, _get_voices);
+ jobjectArray *arr = reinterpret_cast<jobjectArray *>(&voices_object);
+
+ jsize len = env->GetArrayLength(*arr);
+ for (int i = 0; i < len; i++) {
+ jstring jStr = (jstring)env->GetObjectArrayElement(*arr, i);
+ String str = jstring_to_string(jStr, env);
+ Vector<String> tokens = str.split(";", true, 2);
+ if (tokens.size() == 2) {
+ Dictionary voice_d;
+ voice_d["name"] = tokens[1];
+ voice_d["id"] = tokens[1];
+ voice_d["language"] = tokens[0];
+ list.push_back(voice_d);
+ }
+ env->DeleteLocalRef(jStr);
+ }
+ }
+ return list;
+}
+
+void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ if (p_interrupt) {
+ stop();
+ }
+
+ if (p_text.is_empty()) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, p_utterance_id);
+ return;
+ }
+
+ ids[p_utterance_id] = p_text.utf16();
+
+ if (_speak) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ jstring jStrT = env->NewStringUTF(p_text.utf8().get_data());
+ jstring jStrV = env->NewStringUTF(p_voice.utf8().get_data());
+ env->CallVoidMethod(tts, _speak, jStrT, jStrV, CLAMP(p_volume, 0, 100), CLAMP(p_pitch, 0.f, 2.f), CLAMP(p_rate, 0.1f, 10.f), p_utterance_id, p_interrupt);
+ }
+}
+
+void TTS_Android::pause() {
+ if (_pause_speaking) {
+ JNIEnv *env = get_jni_env();
+
+ ERR_FAIL_COND(env == nullptr);
+ env->CallVoidMethod(tts, _pause_speaking);
+ }
+}
+
+void TTS_Android::resume() {
+ if (_resume_speaking) {
+ JNIEnv *env = get_jni_env();
+
+ ERR_FAIL_COND(env == nullptr);
+ env->CallVoidMethod(tts, _resume_speaking);
+ }
+}
+
+void TTS_Android::stop() {
+ for (const KeyValue<int, Char16String> &E : ids) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key);
+ }
+ ids.clear();
+
+ if (_stop_speaking) {
+ JNIEnv *env = get_jni_env();
+
+ ERR_FAIL_COND(env == nullptr);
+ env->CallVoidMethod(tts, _stop_speaking);
+ }
+}
diff --git a/platform/android/tts_android.h b/platform/android/tts_android.h
new file mode 100644
index 0000000000..bc0cdb8d55
--- /dev/null
+++ b/platform/android/tts_android.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* tts_android.h */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#ifndef TTS_ANDROID_H
+#define TTS_ANDROID_H
+
+#include "core/string/ustring.h"
+#include "core/variant/array.h"
+#include "servers/display_server.h"
+
+#include <jni.h>
+
+class TTS_Android {
+ static jobject tts;
+ static jclass cls;
+
+ static jmethodID _is_speaking;
+ static jmethodID _is_paused;
+ static jmethodID _get_voices;
+ static jmethodID _speak;
+ static jmethodID _pause_speaking;
+ static jmethodID _resume_speaking;
+ static jmethodID _stop_speaking;
+
+ static HashMap<int, Char16String> ids;
+
+public:
+ static void setup(jobject p_tts);
+ static void _java_utterance_callback(int p_event, int p_id, int p_pos);
+
+ static bool is_speaking();
+ static bool is_paused();
+ static Array get_voices();
+ static void speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt);
+ static void pause();
+ static void resume();
+ static void stop();
+};
+
+#endif // TTS_ANDROID_H
diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp
index e24d1a4527..5945421e17 100644
--- a/platform/android/vulkan/vulkan_context_android.cpp
+++ b/platform/android/vulkan/vulkan_context_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/platform/android/vulkan/vulkan_context_android.h b/platform/android/vulkan/vulkan_context_android.h
index 182ce33c97..43b5d62598 100644
--- a/platform/android/vulkan/vulkan_context_android.h
+++ b/platform/android/vulkan/vulkan_context_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */