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.cpp100
-rw-r--r--platform/android/android_input_handler.h14
-rw-r--r--platform/android/android_keys_utils.cpp10
-rw-r--r--platform/android/android_keys_utils.h232
-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.h10
-rw-r--r--platform/android/detect.py360
-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.cpp144
-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.cpp402
-rw-r--r--platform/android/export/export_plugin.h37
-rw-r--r--platform/android/export/godot_plugin_config.cpp12
-rw-r--r--platform/android/export/godot_plugin_config.h6
-rw-r--r--platform/android/export/gradle_export_util.cpp115
-rw-r--r--platform/android/export/gradle_export_util.h27
-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.xml22
-rw-r--r--platform/android/java/app/build.gradle51
-rw-r--r--platform/android/java/app/config.gradle104
-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.gradle177
-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.xml73
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt146
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt (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.kt40
-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/gradle/wrapper/gradle-wrapper.jarbin54329 -> 59203 bytes
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xplatform/android/java/gradlew53
-rw-r--r--platform/android/java/gradlew.bat47
-rw-r--r--platform/android/java/lib/AndroidManifest.xml16
-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.java209
-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.java26
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java32
-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.java90
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java25
-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)28
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java13
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java16
-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.java10
-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.kt6
-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.java6
-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.java9
-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.cpp154
-rw-r--r--platform/android/java_godot_lib_jni.h12
-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.cpp140
-rw-r--r--platform/android/os_android.h32
-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
120 files changed, 6054 insertions, 1893 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 e03375e8d9..81802298d9 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,13 +39,10 @@ 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, (HatMask)p_event.hat);
+ Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
break;
default:
return;
@@ -82,37 +79,37 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p
Ref<InputEventKey> ev;
ev.instantiate();
int val = unicode;
- int keycode = android_get_keysym(p_keycode);
- int phy_keycode = android_get_keysym(p_scancode);
+ Key keycode = android_get_keysym(p_keycode);
+ Key phy_keycode = android_get_keysym(p_scancode);
- if (keycode == KEY_SHIFT) {
+ if (keycode == Key::SHIFT) {
shift_mem = p_pressed;
}
- if (keycode == KEY_ALT) {
+ if (keycode == Key::ALT) {
alt_mem = p_pressed;
}
- if (keycode == KEY_CTRL) {
+ if (keycode == Key::CTRL) {
control_mem = p_pressed;
}
- if (keycode == KEY_META) {
+ if (keycode == Key::META) {
meta_mem = p_pressed;
}
- ev->set_keycode((Key)keycode);
- ev->set_physical_keycode((Key)phy_keycode);
+ ev->set_keycode(keycode);
+ ev->set_physical_keycode(phy_keycode);
ev->set_unicode(val);
ev->set_pressed(p_pressed);
_set_key_modifier_state(ev);
if (val == '\n') {
- ev->set_keycode(KEY_ENTER);
+ ev->set_keycode(Key::ENTER);
} else if (val == 61448) {
- ev->set_keycode(KEY_BACKSPACE);
- ev->set_unicode(KEY_BACKSPACE);
+ ev->set_keycode(Key::BACKSPACE);
+ ev->set_unicode((char32_t)Key::BACKSPACE);
} else if (val == 61453) {
- ev->set_keycode(KEY_ENTER);
- ev->set_unicode(KEY_ENTER);
+ ev->set_keycode(Key::ENTER);
+ ev->set_unicode((char32_t)Key::ENTER);
} else if (p_keycode == 4) {
if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) {
dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST, true);
@@ -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();
@@ -223,7 +221,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
ev->set_pressed(false);
ev->set_position(touch[i].pos);
Input::get_singleton()->parse_input_event(ev);
- touch.remove(i);
+ touch.remove_at(i);
break;
}
@@ -305,15 +303,15 @@ void AndroidInputHandler::process_mouse_event(int input_device, int event_action
ev->set_pressed(true);
buttons_state = event_buttons_mask;
if (event_vertical_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_UP, event_vertical_factor);
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, event_vertical_factor);
} else if (event_vertical_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_DOWN, -event_vertical_factor);
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor);
}
if (event_horizontal_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_RIGHT, event_horizontal_factor);
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor);
} else if (event_horizontal_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_LEFT, -event_horizontal_factor);
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor);
}
} break;
}
@@ -323,7 +321,7 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co
Ref<InputEventMouseButton> evd = ev->duplicate();
_set_key_modifier_state(evd);
evd->set_button_index(wheel_button);
- evd->set_button_mask(MouseButton(event_buttons_mask ^ (1 << (wheel_button - 1))));
+ evd->set_button_mask(MouseButton(event_buttons_mask ^ mouse_button_to_mask(wheel_button)));
evd->set_factor(factor);
Input::get_singleton()->parse_input_event(evd);
Ref<InputEventMouseButton> evdd = evd->duplicate();
@@ -339,7 +337,7 @@ void AndroidInputHandler::process_double_tap(int event_android_button_mask, Poin
_set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
- ev->set_pressed(event_button_mask != 0);
+ ev->set_pressed(event_button_mask != MouseButton::NONE);
ev->set_button_index(_button_index_from_mask(event_button_mask));
ev->set_button_mask(event_button_mask);
ev->set_double_click(true);
@@ -348,48 +346,38 @@ void AndroidInputHandler::process_double_tap(int event_android_button_mask, Poin
MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
switch (button_mask) {
- case MOUSE_BUTTON_MASK_LEFT:
- return MOUSE_BUTTON_LEFT;
- case MOUSE_BUTTON_MASK_RIGHT:
- return MOUSE_BUTTON_RIGHT;
- case MOUSE_BUTTON_MASK_MIDDLE:
- return MOUSE_BUTTON_MIDDLE;
- case MOUSE_BUTTON_MASK_XBUTTON1:
- return MOUSE_BUTTON_XBUTTON1;
- case MOUSE_BUTTON_MASK_XBUTTON2:
- return MOUSE_BUTTON_XBUTTON2;
+ case MouseButton::MASK_LEFT:
+ return MouseButton::LEFT;
+ case MouseButton::MASK_RIGHT:
+ return MouseButton::RIGHT;
+ case MouseButton::MASK_MIDDLE:
+ return MouseButton::MIDDLE;
+ case MouseButton::MASK_XBUTTON1:
+ return MouseButton::MB_XBUTTON1;
+ case MouseButton::MASK_XBUTTON2:
+ return MouseButton::MB_XBUTTON2;
default:
- return MOUSE_BUTTON_NONE;
+ return MouseButton::NONE;
}
}
MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
- MouseButton godot_button_mask = MOUSE_BUTTON_NONE;
+ MouseButton godot_button_mask = MouseButton::NONE;
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
- godot_button_mask |= MOUSE_BUTTON_MASK_LEFT;
+ godot_button_mask |= MouseButton::MASK_LEFT;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
- godot_button_mask |= MOUSE_BUTTON_MASK_RIGHT;
+ godot_button_mask |= MouseButton::MASK_RIGHT;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
- godot_button_mask |= MOUSE_BUTTON_MASK_MIDDLE;
+ godot_button_mask |= MouseButton::MASK_MIDDLE;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
- godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1;
+ godot_button_mask |= MouseButton::MASK_XBUTTON1;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
- godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2;
+ godot_button_mask |= MouseButton::MASK_XBUTTON2;
}
return godot_button_mask;
}
-
-void AndroidInputHandler::process_scroll(Point2 p_pos) {
- Ref<InputEventPanGesture> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_delta(p_pos - scroll_prev_pos);
- Input::get_singleton()->parse_input_event(ev);
- scroll_prev_pos = p_pos;
-}
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 2918ca300b..1397ca59e4 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 */
@@ -53,10 +53,10 @@ public:
struct JoypadEvent {
int device = 0;
int type = 0;
- int index = 0;
+ int index = 0; // Can be either JoyAxis or JoyButton.
bool pressed = false;
float value = 0;
- int hat = 0;
+ HatMask hat = HatMask::CENTER;
};
private:
@@ -65,11 +65,10 @@ private:
bool control_mem = false;
bool meta_mem = false;
- MouseButton buttons_state = MOUSE_BUTTON_NONE;
+ MouseButton buttons_state = MouseButton::NONE;
Vector<TouchPos> touch;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
- Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
@@ -83,9 +82,8 @@ public:
void process_hover(int p_type, Point2 p_pos);
void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
void process_double_tap(int event_android_button_mask, Point2 p_pos);
- void process_scroll(Point2 p_pos);
void process_joy_event(JoypadEvent p_event);
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 5aa546c17b..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 */
@@ -30,12 +30,12 @@
#include "android_keys_utils.h"
-unsigned int android_get_keysym(unsigned int p_code) {
- for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
+Key android_get_keysym(unsigned int p_code) {
+ for (int i = 0; _ak_to_keycode[i].keysym != Key::UNKNOWN; i++) {
if (_ak_to_keycode[i].keycode == p_code) {
return _ak_to_keycode[i].keysym;
}
}
- return KEY_UNKNOWN;
+ return Key::UNKNOWN;
}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index 6d25a366a4..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 */
@@ -35,128 +35,128 @@
#include <core/os/keyboard.h>
struct _WinTranslatePair {
- unsigned int keysym = 0;
+ Key keysym = Key::NONE;
unsigned int keycode = 0;
};
static _WinTranslatePair _ak_to_keycode[] = {
- { KEY_TAB, AKEYCODE_TAB },
- { KEY_ENTER, AKEYCODE_ENTER },
- { KEY_SHIFT, AKEYCODE_SHIFT_LEFT },
- { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT },
- { KEY_ALT, AKEYCODE_ALT_LEFT },
- { KEY_ALT, AKEYCODE_ALT_RIGHT },
- { KEY_MENU, AKEYCODE_MENU },
- { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE },
- { KEY_ESCAPE, AKEYCODE_BACK },
- { KEY_SPACE, AKEYCODE_SPACE },
- { KEY_PAGEUP, AKEYCODE_PAGE_UP },
- { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN },
- { KEY_HOME, AKEYCODE_HOME }, //(0x24)
- { KEY_LEFT, AKEYCODE_DPAD_LEFT },
- { KEY_UP, AKEYCODE_DPAD_UP },
- { KEY_RIGHT, AKEYCODE_DPAD_RIGHT },
- { KEY_DOWN, AKEYCODE_DPAD_DOWN },
- { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER },
- { KEY_BACKSPACE, AKEYCODE_DEL },
- { KEY_0, AKEYCODE_0 }, ////0 key
- { KEY_1, AKEYCODE_1 }, ////1 key
- { KEY_2, AKEYCODE_2 }, ////2 key
- { KEY_3, AKEYCODE_3 }, ////3 key
- { KEY_4, AKEYCODE_4 }, ////4 key
- { KEY_5, AKEYCODE_5 }, ////5 key
- { KEY_6, AKEYCODE_6 }, ////6 key
- { KEY_7, AKEYCODE_7 }, ////7 key
- { KEY_8, AKEYCODE_8 }, ////8 key
- { KEY_9, AKEYCODE_9 }, ////9 key
- { KEY_A, AKEYCODE_A }, ////A key
- { KEY_B, AKEYCODE_B }, ////B key
- { KEY_C, AKEYCODE_C }, ////C key
- { KEY_D, AKEYCODE_D }, ////D key
- { KEY_E, AKEYCODE_E }, ////E key
- { KEY_F, AKEYCODE_F }, ////F key
- { KEY_G, AKEYCODE_G }, ////G key
- { KEY_H, AKEYCODE_H }, ////H key
- { KEY_I, AKEYCODE_I }, ////I key
- { KEY_J, AKEYCODE_J }, ////J key
- { KEY_K, AKEYCODE_K }, ////K key
- { KEY_L, AKEYCODE_L }, ////L key
- { KEY_M, AKEYCODE_M }, ////M key
- { KEY_N, AKEYCODE_N }, ////N key
- { KEY_O, AKEYCODE_O }, ////O key
- { KEY_P, AKEYCODE_P }, ////P key
- { KEY_Q, AKEYCODE_Q }, ////Q key
- { KEY_R, AKEYCODE_R }, ////R key
- { KEY_S, AKEYCODE_S }, ////S key
- { KEY_T, AKEYCODE_T }, ////T key
- { KEY_U, AKEYCODE_U }, ////U key
- { KEY_V, AKEYCODE_V }, ////V key
- { KEY_W, AKEYCODE_W }, ////W key
- { KEY_X, AKEYCODE_X }, ////X key
- { KEY_Y, AKEYCODE_Y }, ////Y key
- { KEY_Z, AKEYCODE_Z }, ////Z key
- { KEY_HOMEPAGE, AKEYCODE_EXPLORER },
- { KEY_LAUNCH0, AKEYCODE_BUTTON_A },
- { KEY_LAUNCH1, AKEYCODE_BUTTON_B },
- { KEY_LAUNCH2, AKEYCODE_BUTTON_C },
- { KEY_LAUNCH3, AKEYCODE_BUTTON_X },
- { KEY_LAUNCH4, AKEYCODE_BUTTON_Y },
- { KEY_LAUNCH5, AKEYCODE_BUTTON_Z },
- { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 },
- { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 },
- { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 },
- { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 },
- { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL },
- { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR },
- { KEY_LAUNCHC, AKEYCODE_BUTTON_START },
- { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT },
- { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE },
- { KEY_VOLUMEMUTE, AKEYCODE_MUTE },
- { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN },
- { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP },
- { KEY_BACK, AKEYCODE_MEDIA_REWIND },
- { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD },
- { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT },
- { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS },
- { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP },
- { KEY_PLUS, AKEYCODE_PLUS },
- { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key
- { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key
- { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key
- { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key
- { KEY_BACKSLASH, AKEYCODE_BACKSLASH },
- { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
- { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
- { KEY_CTRL, AKEYCODE_CTRL_LEFT },
- { KEY_CTRL, AKEYCODE_CTRL_RIGHT },
- { KEY_UNKNOWN, 0 }
+ { Key::TAB, AKEYCODE_TAB },
+ { Key::ENTER, AKEYCODE_ENTER },
+ { Key::SHIFT, AKEYCODE_SHIFT_LEFT },
+ { Key::SHIFT, AKEYCODE_SHIFT_RIGHT },
+ { Key::ALT, AKEYCODE_ALT_LEFT },
+ { Key::ALT, AKEYCODE_ALT_RIGHT },
+ { Key::MENU, AKEYCODE_MENU },
+ { Key::PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE },
+ { Key::ESCAPE, AKEYCODE_BACK },
+ { Key::SPACE, AKEYCODE_SPACE },
+ { Key::PAGEUP, AKEYCODE_PAGE_UP },
+ { Key::PAGEDOWN, AKEYCODE_PAGE_DOWN },
+ { Key::HOME, AKEYCODE_HOME }, //(0x24)
+ { Key::LEFT, AKEYCODE_DPAD_LEFT },
+ { Key::UP, AKEYCODE_DPAD_UP },
+ { Key::RIGHT, AKEYCODE_DPAD_RIGHT },
+ { Key::DOWN, AKEYCODE_DPAD_DOWN },
+ { Key::PERIODCENTERED, AKEYCODE_DPAD_CENTER },
+ { Key::BACKSPACE, AKEYCODE_DEL },
+ { Key::KEY_0, AKEYCODE_0 },
+ { Key::KEY_1, AKEYCODE_1 },
+ { Key::KEY_2, AKEYCODE_2 },
+ { Key::KEY_3, AKEYCODE_3 },
+ { Key::KEY_4, AKEYCODE_4 },
+ { Key::KEY_5, AKEYCODE_5 },
+ { Key::KEY_6, AKEYCODE_6 },
+ { Key::KEY_7, AKEYCODE_7 },
+ { Key::KEY_8, AKEYCODE_8 },
+ { Key::KEY_9, AKEYCODE_9 },
+ { Key::A, AKEYCODE_A },
+ { Key::B, AKEYCODE_B },
+ { Key::C, AKEYCODE_C },
+ { Key::D, AKEYCODE_D },
+ { Key::E, AKEYCODE_E },
+ { Key::F, AKEYCODE_F },
+ { Key::G, AKEYCODE_G },
+ { Key::H, AKEYCODE_H },
+ { Key::I, AKEYCODE_I },
+ { Key::J, AKEYCODE_J },
+ { Key::K, AKEYCODE_K },
+ { Key::L, AKEYCODE_L },
+ { Key::M, AKEYCODE_M },
+ { Key::N, AKEYCODE_N },
+ { Key::O, AKEYCODE_O },
+ { Key::P, AKEYCODE_P },
+ { Key::Q, AKEYCODE_Q },
+ { Key::R, AKEYCODE_R },
+ { Key::S, AKEYCODE_S },
+ { Key::T, AKEYCODE_T },
+ { Key::U, AKEYCODE_U },
+ { Key::V, AKEYCODE_V },
+ { Key::W, AKEYCODE_W },
+ { Key::X, AKEYCODE_X },
+ { Key::Y, AKEYCODE_Y },
+ { Key::Z, AKEYCODE_Z },
+ { Key::HOMEPAGE, AKEYCODE_EXPLORER },
+ { Key::LAUNCH0, AKEYCODE_BUTTON_A },
+ { Key::LAUNCH1, AKEYCODE_BUTTON_B },
+ { Key::LAUNCH2, AKEYCODE_BUTTON_C },
+ { Key::LAUNCH3, AKEYCODE_BUTTON_X },
+ { Key::LAUNCH4, AKEYCODE_BUTTON_Y },
+ { Key::LAUNCH5, AKEYCODE_BUTTON_Z },
+ { Key::LAUNCH6, AKEYCODE_BUTTON_L1 },
+ { Key::LAUNCH7, AKEYCODE_BUTTON_R1 },
+ { Key::LAUNCH8, AKEYCODE_BUTTON_L2 },
+ { Key::LAUNCH9, AKEYCODE_BUTTON_R2 },
+ { Key::LAUNCHA, AKEYCODE_BUTTON_THUMBL },
+ { Key::LAUNCHB, AKEYCODE_BUTTON_THUMBR },
+ { Key::LAUNCHC, AKEYCODE_BUTTON_START },
+ { Key::LAUNCHD, AKEYCODE_BUTTON_SELECT },
+ { Key::LAUNCHE, AKEYCODE_BUTTON_MODE },
+ { Key::VOLUMEMUTE, AKEYCODE_MUTE },
+ { Key::VOLUMEDOWN, AKEYCODE_VOLUME_DOWN },
+ { Key::VOLUMEUP, AKEYCODE_VOLUME_UP },
+ { Key::BACK, AKEYCODE_MEDIA_REWIND },
+ { Key::FORWARD, AKEYCODE_MEDIA_FAST_FORWARD },
+ { Key::MEDIANEXT, AKEYCODE_MEDIA_NEXT },
+ { Key::MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS },
+ { Key::MEDIASTOP, AKEYCODE_MEDIA_STOP },
+ { Key::PLUS, AKEYCODE_PLUS },
+ { Key::EQUAL, AKEYCODE_EQUALS }, // the '+' key
+ { Key::COMMA, AKEYCODE_COMMA }, // the ',' key
+ { Key::MINUS, AKEYCODE_MINUS }, // the '-' key
+ { Key::SLASH, AKEYCODE_SLASH }, // the '/?' key
+ { Key::BACKSLASH, AKEYCODE_BACKSLASH },
+ { Key::BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
+ { Key::BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
+ { Key::CTRL, AKEYCODE_CTRL_LEFT },
+ { Key::CTRL, AKEYCODE_CTRL_RIGHT },
+ { Key::UNKNOWN, 0 }
};
/*
TODO: map these android key:
- AKEYCODE_SOFT_LEFT = 1,
- AKEYCODE_SOFT_RIGHT = 2,
- AKEYCODE_CALL = 5,
- AKEYCODE_ENDCALL = 6,
- AKEYCODE_STAR = 17,
- AKEYCODE_POUND = 18,
- AKEYCODE_POWER = 26,
- AKEYCODE_CAMERA = 27,
- AKEYCODE_CLEAR = 28,
- AKEYCODE_SYM = 63,
- AKEYCODE_ENVELOPE = 65,
- AKEYCODE_GRAVE = 68,
- AKEYCODE_SEMICOLON = 74,
- AKEYCODE_APOSTROPHE = 75,
- AKEYCODE_AT = 77,
- AKEYCODE_NUM = 78,
- AKEYCODE_HEADSETHOOK = 79,
- AKEYCODE_FOCUS = 80, // *Camera* focus
- AKEYCODE_NOTIFICATION = 83,
- AKEYCODE_SEARCH = 84,
- AKEYCODE_PICTSYMBOLS = 94,
- AKEYCODE_SWITCH_CHARSET = 95,
+ AKEYCODE_SOFT_LEFT = 1,
+ AKEYCODE_SOFT_RIGHT = 2,
+ AKEYCODE_CALL = 5,
+ AKEYCODE_ENDCALL = 6,
+ AKEYCODE_STAR = 17,
+ AKEYCODE_POUND = 18,
+ AKEYCODE_POWER = 26,
+ AKEYCODE_CAMERA = 27,
+ AKEYCODE_CLEAR = 28,
+ AKEYCODE_SYM = 63,
+ AKEYCODE_ENVELOPE = 65,
+ AKEYCODE_GRAVE = 68,
+ AKEYCODE_SEMICOLON = 74,
+ AKEYCODE_APOSTROPHE = 75,
+ AKEYCODE_AT = 77,
+ AKEYCODE_NUM = 78,
+ AKEYCODE_HEADSETHOOK = 79,
+ AKEYCODE_FOCUS = 80, // *Camera* focus
+ AKEYCODE_NOTIFICATION = 83,
+ AKEYCODE_SEARCH = 84,
+ AKEYCODE_PICTSYMBOLS = 94,
+ AKEYCODE_SWITCH_CHARSET = 95,
*/
-unsigned int android_get_keysym(unsigned int p_code);
+Key android_get_keysym(unsigned int p_code);
#endif // ANDROID_KEYS_UTILS_H
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 e3efaddba2..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 */
@@ -59,7 +59,6 @@ class AudioDriverOpenSL : public AudioDriver {
SLObjectItf sl;
SLEngineItf EngineItf;
SLObjectItf OutputMix;
- SLVolumeItf volumeItf;
SLObjectItf player;
SLObjectItf recorder;
SLAndroidSimpleBufferQueueItf bufferQueueItf;
@@ -68,7 +67,6 @@ class AudioDriverOpenSL : public AudioDriver {
SLDataFormat_PCM pcm;
SLDataSink audioSink;
SLDataLocator_OutputMix locator_outputmix;
- SLBufferQueueState state;
static AudioDriverOpenSL *s_ad;
@@ -89,8 +87,6 @@ class AudioDriverOpenSL : public AudioDriver {
virtual Error capture_init_device();
public:
- void set_singleton();
-
virtual const char *get_name() const;
virtual Error init();
@@ -109,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 61ccad9ac3..47cfade765 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -1,7 +1,7 @@
import os
import sys
import platform
-from distutils.version import LooseVersion
+import subprocess
def is_active():
@@ -13,42 +13,35 @@ def get_name():
def can_build():
- return ("ANDROID_SDK_ROOT" in os.environ) or ("ANDROID_HOME" in os.environ)
-
-
-def get_platform(platform):
- return int(platform.split("-")[1])
+ return os.path.exists(get_env_android_sdk_root())
def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
- ("ANDROID_NDK_ROOT", "Path to the Android NDK", get_android_ndk_root()),
- ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_android_sdk_root()),
+ ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
- EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")),
- BoolVariable("android_neon", "Enable NEON support (armv7 only)", True),
+ EnumVariable("android_arch", "Target architecture", "arm64v8", ("armv7", "arm64v8", "x86", "x86_64")),
]
# Return the ANDROID_SDK_ROOT environment variable.
-# While ANDROID_HOME has been deprecated, it's used as a fallback for backward
-# compatibility purposes.
-def get_android_sdk_root():
- if "ANDROID_SDK_ROOT" in os.environ:
- return os.environ.get("ANDROID_SDK_ROOT", 0)
- else:
- return os.environ.get("ANDROID_HOME", 0)
+def get_env_android_sdk_root():
+ return os.environ.get("ANDROID_SDK_ROOT", -1)
-# Return the ANDROID_NDK_ROOT environment variable.
-# We generate one for this build using the ANDROID_SDK_ROOT env
-# variable and the project ndk version.
-# If the env variable is already defined, we override it with
-# our own to match what the project expects.
-def get_android_ndk_root():
- return get_android_sdk_root() + "/ndk/" + get_project_ndk_version()
+def get_min_sdk_version(platform):
+ return int(platform.split("-")[1])
+
+
+def get_android_ndk_root(env):
+ return env["ANDROID_SDK_ROOT"] + "/ndk/" + get_ndk_version()
+
+
+# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
+def get_ndk_version():
+ return "23.2.8568313"
def get_flags():
@@ -57,159 +50,87 @@ def get_flags():
]
-def create(env):
- tools = env["TOOLS"]
- if "mingw" in tools:
- tools.remove("mingw")
- if "applelink" in tools:
- tools.remove("applelink")
- env.Tool("gcc")
- return env.Clone(tools=tools)
-
-
-# Check if ANDROID_NDK_ROOT is valid.
-# If not, install the ndk using ANDROID_SDK_ROOT and sdkmanager.
+# Check if Android NDK version is installed
+# If not, install it.
def install_ndk_if_needed(env):
print("Checking for Android NDK...")
- env_ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"])
- if env_ndk_version is None:
- # Reinstall the ndk and update ANDROID_NDK_ROOT.
- print("Installing Android NDK...")
- if env["ANDROID_SDK_ROOT"] is None:
- raise Exception("Invalid ANDROID_SDK_ROOT environment variable.")
-
- import subprocess
-
+ sdk_root = env["ANDROID_SDK_ROOT"]
+ if not os.path.exists(get_android_ndk_root(env)):
extension = ".bat" if os.name == "nt" else ""
- sdkmanager_path = env["ANDROID_SDK_ROOT"] + "/cmdline-tools/latest/bin/sdkmanager" + extension
- ndk_download_args = "ndk;" + get_project_ndk_version()
- subprocess.check_call([sdkmanager_path, ndk_download_args])
-
- env["ANDROID_NDK_ROOT"] = env["ANDROID_SDK_ROOT"] + "/ndk/" + get_project_ndk_version()
- print("ANDROID_NDK_ROOT: " + env["ANDROID_NDK_ROOT"])
+ sdkmanager = sdk_root + "/cmdline-tools/latest/bin/sdkmanager" + extension
+ if os.path.exists(sdkmanager):
+ # Install the Android NDK
+ print("Installing Android NDK...")
+ ndk_download_args = "ndk;" + get_ndk_version()
+ subprocess.check_call([sdkmanager, ndk_download_args])
+ else:
+ print("Cannot find " + sdkmanager)
+ print(
+ "Please ensure ANDROID_SDK_ROOT is correct and cmdline-tools are installed, or install NDK version "
+ + get_ndk_version()
+ + " manually."
+ )
+ sys.exit()
+ env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
def configure(env):
install_ndk_if_needed(env)
-
- # Workaround for MinGW. See:
- # https://www.scons.org/wiki/LongCmdLinesOnWin32
- if os.name == "nt":
-
- import subprocess
-
- def mySubProcess(cmdline, env):
- # print("SPAWNED : " + cmdline)
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- proc = subprocess.Popen(
- cmdline,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- startupinfo=startupinfo,
- shell=False,
- env=env,
- )
- data, err = proc.communicate()
- rv = proc.wait()
- if rv:
- print("=====")
- print(err)
- print("=====")
- return rv
-
- def mySpawn(sh, escape, cmd, args, env):
-
- newargs = " ".join(args[1:])
- cmdline = cmd + " " + newargs
-
- rv = 0
- if len(cmdline) > 32000 and cmd.endswith("ar"):
- cmdline = cmd + " " + args[1] + " " + args[2] + " "
- for i in range(3, len(args)):
- rv = mySubProcess(cmdline + args[i], env)
- if rv:
- break
- else:
- rv = mySubProcess(cmdline, env)
-
- return rv
-
- env["SPAWN"] = mySpawn
+ ndk_root = env["ANDROID_NDK_ROOT"]
# Architecture
if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]:
- env["android_arch"] = "armv7"
+ env["android_arch"] = "arm64v8"
- neon_text = ""
- if env["android_arch"] == "armv7" and env["android_neon"]:
- neon_text = " (with NEON)"
- print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")" + neon_text)
+ print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")")
- can_vectorize = True
- if env["android_arch"] == "x86":
- env["ARCH"] = "arch-x86"
- env.extra_suffix = ".x86" + env.extra_suffix
- target_subpath = "x86-4.9"
- abi_subpath = "i686-linux-android"
- arch_subpath = "x86"
- env["x86_libtheora_opt_gcc"] = True
- if env["android_arch"] == "x86_64":
- if get_platform(env["ndk_platform"]) < 21:
+ if get_min_sdk_version(env["ndk_platform"]) < 21:
+ if env["android_arch"] == "x86_64" or env["android_arch"] == "arm64v8":
print(
- "WARNING: android_arch=x86_64 is not supported by ndk_platform lower than android-21; setting"
- " ndk_platform=android-21"
+ "WARNING: android_arch="
+ + env["android_arch"]
+ + " is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21"
)
env["ndk_platform"] = "android-21"
- env["ARCH"] = "arch-x86_64"
- env.extra_suffix = ".x86_64" + env.extra_suffix
- target_subpath = "x86_64-4.9"
- abi_subpath = "x86_64-linux-android"
- arch_subpath = "x86_64"
- env["x86_libtheora_opt_gcc"] = True
- elif env["android_arch"] == "armv7":
- env["ARCH"] = "arch-arm"
- target_subpath = "arm-linux-androideabi-4.9"
- abi_subpath = "arm-linux-androideabi"
- arch_subpath = "armeabi-v7a"
- if env["android_neon"]:
- env.extra_suffix = ".armv7.neon" + env.extra_suffix
- else:
- env.extra_suffix = ".armv7" + env.extra_suffix
+
+ if env["android_arch"] == "armv7":
+ target_triple = "armv7a-linux-androideabi"
+ bin_utils = "arm-linux-androideabi"
+ env.extra_suffix = ".armv7" + env.extra_suffix
elif env["android_arch"] == "arm64v8":
- if get_platform(env["ndk_platform"]) < 21:
- print(
- "WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than android-21; setting"
- " ndk_platform=android-21"
- )
- env["ndk_platform"] = "android-21"
- env["ARCH"] = "arch-arm64"
- target_subpath = "aarch64-linux-android-4.9"
- abi_subpath = "aarch64-linux-android"
- arch_subpath = "arm64-v8a"
+ target_triple = "aarch64-linux-android"
+ bin_utils = target_triple
env.extra_suffix = ".armv8" + env.extra_suffix
+ elif env["android_arch"] == "x86":
+ target_triple = "i686-linux-android"
+ bin_utils = target_triple
+ env.extra_suffix = ".x86" + env.extra_suffix
+ elif env["android_arch"] == "x86_64":
+ target_triple = "x86_64-linux-android"
+ bin_utils = target_triple
+ env.extra_suffix = ".x86_64" + env.extra_suffix
+
+ target_option = ["-target", target_triple + str(get_min_sdk_version(env["ndk_platform"]))]
+ env.Append(CCFLAGS=target_option)
+ env.Append(LINKFLAGS=target_option)
# Build type
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(CCFLAGS=[opt, "-fomit-frame-pointer"])
elif env["optimize"] == "size": # optimize for size
- env.Append(CCFLAGS=["-Os"])
- env.Append(LINKFLAGS=["-Os"])
-
+ env.Append(CCFLAGS=["-Oz"])
env.Append(CPPDEFINES=["NDEBUG"])
- if can_vectorize:
- env.Append(CCFLAGS=["-ftree-vectorize"])
- if env["target"] == "release_debug":
- env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CCFLAGS=["-ftree-vectorize"])
elif env["target"] == "debug":
env.Append(LINKFLAGS=["-O0"])
env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
- env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["_DEBUG"])
env.Append(CPPFLAGS=["-UNDEBUG"])
# Compiler configuration
@@ -217,7 +138,6 @@ def configure(env):
env["SHLIBSUFFIX"] = ".so"
if env["PLATFORM"] == "win32":
- env.Tool("gcc")
env.use_windows_spawn_fix()
if sys.platform.startswith("linux"):
@@ -230,32 +150,15 @@ def configure(env):
else:
host_subpath = "windows"
- compiler_path = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/" + host_subpath + "/bin"
- gcc_toolchain_path = env["ANDROID_NDK_ROOT"] + "/toolchains/" + target_subpath + "/prebuilt/" + host_subpath
- tools_path = gcc_toolchain_path + "/" + abi_subpath + "/bin"
-
- # For Clang to find NDK tools in preference of those system-wide
- env.PrependENVPath("PATH", tools_path)
-
- ccache_path = os.environ.get("CCACHE")
- if ccache_path is None:
- env["CC"] = compiler_path + "/clang"
- env["CXX"] = compiler_path + "/clang++"
- else:
- # there aren't any ccache wrappers available for Android,
- # to enable caching we need to prepend the path to the ccache binary
- env["CC"] = ccache_path + " " + compiler_path + "/clang"
- env["CXX"] = ccache_path + " " + compiler_path + "/clang++"
- env["AR"] = tools_path + "/ar"
- env["RANLIB"] = tools_path + "/ranlib"
- env["AS"] = tools_path + "/as"
+ toolchain_path = ndk_root + "/toolchains/llvm/prebuilt/" + host_subpath
+ compiler_path = toolchain_path + "/bin"
+ bin_utils_path = toolchain_path + "/" + bin_utils + "/bin"
- common_opts = ["-gcc-toolchain", gcc_toolchain_path]
-
- # Compile flags
-
- env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/include"])
- env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
+ env["CC"] = compiler_path + "/clang"
+ env["CXX"] = compiler_path + "/clang++"
+ env["AR"] = compiler_path + "/llvm-ar"
+ env["RANLIB"] = compiler_path + "/llvm-ranlib"
+ env["AS"] = bin_utils_path + "/as"
# Disable exceptions and rtti on non-tools (template) builds
if env["tools"]:
@@ -267,104 +170,31 @@ def configure(env):
# Don't use dynamic_cast, necessary with no-rtti.
env.Append(CPPDEFINES=["NO_SAFE_CAST"])
- lib_sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env["ndk_platform"] + "/" + env["ARCH"]
-
- # Using NDK unified headers (NDK r15+)
- sysroot = env["ANDROID_NDK_ROOT"] + "/sysroot"
- env.Append(CPPFLAGS=["--sysroot=" + sysroot])
- env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include/" + abi_subpath])
- env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/android/support/include"])
- # For unified headers this define has to be set manually
- env.Append(CPPDEFINES=[("__ANDROID_API__", str(get_platform(env["ndk_platform"])))])
-
env.Append(
CCFLAGS=(
- "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden"
- " -fno-strict-aliasing".split()
+ "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
)
)
env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
- if get_platform(env["ndk_platform"]) >= 24:
+ if get_min_sdk_version(env["ndk_platform"]) >= 24:
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
- env["neon_enabled"] = False
if env["android_arch"] == "x86":
- target_opts = ["-target", "i686-none-linux-android"]
- # The NDK adds this if targeting API < 21, so we can drop it when Godot targets it at least
+ # The NDK adds this if targeting API < 24, so we can drop it when Godot targets it at least
env.Append(CCFLAGS=["-mstackrealign"])
-
- elif env["android_arch"] == "x86_64":
- target_opts = ["-target", "x86_64-none-linux-android"]
-
elif env["android_arch"] == "armv7":
- target_opts = ["-target", "armv7-none-linux-androideabi"]
env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
- if env["android_neon"]:
- env["neon_enabled"] = True
- env.Append(CCFLAGS=["-mfpu=neon"])
- env.Append(CPPDEFINES=["__ARM_NEON__"])
- else:
- env.Append(CCFLAGS=["-mfpu=vfpv3-d16"])
-
+ env.Append(CPPDEFINES=["__ARM_NEON__"])
elif env["android_arch"] == "arm64v8":
- target_opts = ["-target", "aarch64-none-linux-android"]
env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
- env.Append(CCFLAGS=target_opts)
- env.Append(CCFLAGS=common_opts)
-
# Link flags
- ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"])
- if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("17.1.4828580"):
- env.Append(LINKFLAGS=["-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"])
- else:
- env.Append(
- LINKFLAGS=[
- env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libandroid_support.a"
- ]
- )
- env.Append(LINKFLAGS=["-shared", "--sysroot=" + lib_sysroot, "-Wl,--warn-shared-textrel"])
- env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/"])
- env.Append(
- LINKFLAGS=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libc++_shared.so"]
- )
-
- if env["android_arch"] == "armv7":
- env.Append(LINKFLAGS="-Wl,--fix-cortex-a8".split())
- env.Append(LINKFLAGS="-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now".split())
- env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so -Wl,--gc-sections".split())
-
- env.Append(LINKFLAGS=target_opts)
- env.Append(LINKFLAGS=common_opts)
-
- env.Append(
- LIBPATH=[
- env["ANDROID_NDK_ROOT"]
- + "/toolchains/"
- + target_subpath
- + "/prebuilt/"
- + host_subpath
- + "/lib/gcc/"
- + abi_subpath
- + "/4.9.x"
- ]
- )
- env.Append(
- LIBPATH=[
- env["ANDROID_NDK_ROOT"]
- + "/toolchains/"
- + target_subpath
- + "/prebuilt/"
- + host_subpath
- + "/"
- + abi_subpath
- + "/lib"
- ]
- )
+ env.Append(LINKFLAGS="-Wl,--gc-sections -Wl,--no-undefined -Wl,-z,now".split())
+ env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so")
env.Prepend(CPPPATH=["#platform/android"])
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"])
@@ -374,25 +204,3 @@ def configure(env):
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
-
-
-# Return the project NDK version.
-# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
-def get_project_ndk_version():
- return "21.4.7075529"
-
-
-# Return NDK version string in source.properties (adapted from the Chromium project).
-def get_env_ndk_version(path):
- if path is None:
- return None
- prop_file_path = os.path.join(path, "source.properties")
- try:
- with open(prop_file_path) as prop_file:
- for line in prop_file:
- key_value = list(map(lambda x: x.strip(), line.split("=")))
- if key_value[0] == "Pkg.Revision":
- return key_value[1]
- except Exception:
- print("Could not read source prop file '%s'" % prop_file_path)
- return None
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index 0eeee8215d..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 == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))
+ 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 == "")
+ } 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 == "")
+ 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 == "")
+ 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 720752d28f..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,16 +162,18 @@ 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);
- return (ScreenOrientation)godot_io_java->get_screen_orientation();
+ const int orientation = godot_io_java->get_screen_orientation();
+ ERR_FAIL_INDEX_V_MSG(orientation, 7, SCREEN_LANDSCAPE, "Unrecognized screen orientation");
+ return (ScreenOrientation)orientation;
}
int DisplayServerAndroid::get_screen_count() const {
@@ -135,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);
@@ -166,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();
@@ -177,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();
}
@@ -241,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;
}
@@ -344,8 +430,8 @@ void DisplayServerAndroid::process_events() {
Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
Vector<String> drivers;
-#ifdef OPENGL_ENABLED
- drivers.push_back("opengl");
+#ifdef GLES3_ENABLED
+ drivers.push_back("opengl3");
#endif
#ifdef VULKAN_ENABLED
drivers.push_back("vulkan");
@@ -370,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);
@@ -407,13 +493,13 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
-#if defined(OPENGL_ENABLED)
- if (rendering_driver == "opengl") {
+#if defined(GLES3_ENABLED)
+ if (rendering_driver == "opengl3") {
bool gl_initialization_error = false;
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
+ if (RasterizerGLES3::is_viable() == OK) {
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
} else {
gl_initialization_error = true;
}
@@ -433,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 60ba1c558a..b40c1f5090 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 },
@@ -224,8 +244,12 @@ static const int EXPORT_FORMAT_AAB = 1;
static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
+static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
+static const int DEFAULT_TARGET_SDK_VERSION = 31; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
+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
@@ -303,7 +327,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
}
}
- if (d.description == "") {
+ if (d.description.is_empty()) {
//in the oven, request!
args.clear();
args.push_back("-s");
@@ -352,7 +376,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
}
d.name = vendor + " " + device;
- if (device == String()) {
+ if (device.is_empty()) {
continue;
}
}
@@ -385,18 +409,18 @@ 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 {
String aname;
- if (p_name != "") {
+ if (!p_name.is_empty()) {
aname = p_name;
} else {
aname = ProjectSettings::get_singleton()->get("application/config/name");
}
- if (aname == "") {
+ if (aname.is_empty()) {
aname = VERSION_NAME;
}
@@ -412,15 +436,15 @@ 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;
}
}
- if (name == "") {
+ if (name.is_empty()) {
name = "noname";
}
@@ -429,9 +453,8 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co
return pname;
}
-String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset) const {
- int export_format = int(p_preset->get("custom_template/export_format"));
- return export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
+String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
+ return p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
}
bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const {
@@ -459,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));
}
@@ -499,11 +522,11 @@ 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
- * zip-aligned, assets stored as they are can be efficiently read by
- * Android by memory-mapping them.
- */
+ * 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.
+ */
// -- Unconditional uncompress to mimic AAPT plus some other
@@ -520,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
};
@@ -573,12 +596,12 @@ 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();
- if (file == "") {
+ if (file.is_empty()) {
break;
}
@@ -662,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) {
@@ -687,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);
@@ -702,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]);
@@ -752,9 +775,9 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset>
}
int xr_mode_index = p_preset->get("xr_features/xr_mode");
- if (xr_mode_index == 1 /* XRMode.OVR */) {
+ if (xr_mode_index == XR_MODE_OPENXR) {
int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
- if (hand_tracking_index > 0) {
+ if (hand_tracking_index > XR_HAND_TRACKING_NONE) {
if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) {
r_permissions.push_back("com.oculus.permission.HAND_TRACKING");
}
@@ -784,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"));
@@ -811,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;
@@ -833,10 +853,14 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ int hand_tracking_index = p_preset->get("xr_features/hand_tracking");
+ int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency");
bool backup_allowed = p_preset->get("user_data_backup/allow");
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.
@@ -852,16 +876,9 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
int iofs = ofs + 8;
string_count = decode_uint32(&p_manifest[iofs]);
- //styles_count = decode_uint32(&p_manifest[iofs + 4]);
string_flags = decode_uint32(&p_manifest[iofs + 8]);
string_data_offset = decode_uint32(&p_manifest[iofs + 12]);
- //styles_offset = decode_uint32(&p_manifest[iofs + 16]);
- /*
- printf("string count: %i\n",string_count);
- printf("flags: %i\n",string_flags);
- printf("sdata ofs: %i\n",string_data_offset);
- printf("styles ofs: %i\n",styles_offset);
- */
+
uint32_t st_offset = iofs + 20;
string_table.resize(string_count);
uint32_t string_end = 0;
@@ -947,14 +964,18 @@ 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]);
}
+ if (tname == "activity" && attrname == "excludeFromRecents") {
+ 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]);
@@ -970,6 +991,25 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
}
}
+ // 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_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_version_name") {
+ string_table.write[attr_value] = "com.oculus.handtracking.version";
+ }
+
+ if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_value") {
+ string_table.write[attr_value] = "V2.0";
+ }
+ }
+
iofs += 20;
}
@@ -984,19 +1024,26 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
Vector<bool> feature_required_list;
Vector<int> feature_versions;
- if (xr_mode_index == 1 /* XRMode.OVR */) {
+ if (xr_mode_index == XR_MODE_OPENXR) {
// Set degrees of freedom
feature_names.push_back("android.hardware.vr.headtracking");
feature_required_list.push_back(true);
feature_versions.push_back(1);
// Check for hand tracking
- int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
- if (hand_tracking_index > 0) {
+ if (hand_tracking_index > XR_HAND_TRACKING_NONE) {
feature_names.push_back("oculus.software.handtracking");
- feature_required_list.push_back(hand_tracking_index == 2);
+ feature_required_list.push_back(hand_tracking_index == XR_HAND_TRACKING_REQUIRED);
feature_versions.push_back(-1); // no version attribute should be added.
}
+
+ // Check for passthrough
+ int passthrough_mode = p_preset->get("xr_features/passthrough");
+ if (passthrough_mode > XR_PASSTHROUGH_NONE) {
+ feature_names.push_back("com.oculus.feature.PASSTHROUGH");
+ feature_required_list.push_back(passthrough_mode == XR_PASSTHROUGH_REQUIRED);
+ feature_versions.push_back(-1);
+ }
}
if (feature_names.size() > 0) {
@@ -1343,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]);
@@ -1357,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);
}
@@ -1476,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
@@ -1618,11 +1665,11 @@ Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExp
void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- if (driver == "GLES2") {
+ if (driver == "opengl3") {
r_features->push_back("etc");
}
// FIXME: Review what texture formats are used for Vulkan.
- if (driver == "Vulkan") {
+ if (driver == "vulkan") {
r_features->push_back("etc2");
}
@@ -1641,15 +1688,17 @@ 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();
- Vector<String> abis = get_abis();
+ const Vector<String> abis = get_abis();
for (int i = 0; i < abis.size(); ++i) {
- String abi = abis[i];
- bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+ const String abi = abis[i];
+ // 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, vformat("%s/%s", PNAME("architectures"), abi)), is_default));
}
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
@@ -1661,21 +1710,26 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/min_sdk", PROPERTY_HINT_RANGE, SDK_VERSION_RANGE), DEFAULT_MIN_SDK_VERSION));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/target_sdk", PROPERTY_HINT_RANGE, SDK_VERSION_RANGE), DEFAULT_TARGET_SDK_VERSION));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,OpenXR"), XR_MODE_REGULAR));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_HAND_TRACKING_NONE));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking_frequency", PROPERTY_HINT_ENUM, "Low,High"), XR_HAND_TRACKING_FREQUENCY_LOW));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/passthrough", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_PASSTHROUGH_NONE));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
@@ -1695,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++;
}
}
@@ -1764,7 +1818,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
String can_export_error;
bool can_export_missing_templates;
if (!can_export(p_preset, can_export_error, can_export_missing_templates)) {
- EditorNode::add_io_error(can_export_error);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), can_export_error);
return ERR_UNCONFIGURED;
}
@@ -1792,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);
@@ -1842,7 +1897,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
print_verbose(output);
if (err || rv != 0) {
- EditorNode::add_io_error(vformat(TTR("Could not install to device: %s"), output));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), vformat(TTR("Could not install to device: %s"), output));
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
@@ -1920,7 +1975,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
print_verbose(output);
if (err || rv != 0) {
- EditorNode::add_io_error(TTR("Could not execute on device."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Could not execute on device."));
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
@@ -1952,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;
@@ -1975,7 +2030,7 @@ String EditorExportPlatformAndroid::get_apksigner_path() {
da->list_dir_end();
if (apksigner_path.is_empty()) {
- EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool."));
+ print_error("Unable to find the 'apksigner' tool.");
}
return apksigner_path;
@@ -1984,10 +2039,11 @@ String EditorExportPlatformAndroid::get_apksigner_path() {
bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
+ const bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
// Look for export templates (first official, and if defined custom templates).
- if (!bool(p_preset->get("custom_template/use_custom_build"))) {
+ if (!custom_build_enabled) {
String template_err;
bool dvalid = false;
bool rvalid = false;
@@ -2062,13 +2118,13 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
}
String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
- if (sdk_path == "") {
+ if (sdk_path.is_empty()) {
err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n";
valid = false;
} 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!");
@@ -2086,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!");
@@ -2109,7 +2165,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
if (apk_expansion) {
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
- if (apk_expansion_pkey == "") {
+ if (apk_expansion_pkey.is_empty()) {
valid = false;
err += TTR("Invalid public key for APK expansion.") + "\n";
@@ -2125,14 +2181,13 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
}
String etc_error = test_etc2();
- if (etc_error != String()) {
+ if (!etc_error.is_empty()) {
valid = false;
err += etc_error;
}
// Ensure that `Use Custom Build` is enabled if a plugin is selected.
String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset));
- bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
if (!enabled_plugins_names.is_empty() && !custom_build_enabled) {
valid = false;
err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
@@ -2142,21 +2197,50 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
// Validate the Xr features are properly populated
int xr_mode_index = p_preset->get("xr_features/xr_mode");
int hand_tracking = p_preset->get("xr_features/hand_tracking");
- if (xr_mode_index != /* XRMode.OVR*/ 1) {
- if (hand_tracking > 0) {
+ int passthrough_mode = p_preset->get("xr_features/passthrough");
+ if (xr_mode_index != XR_MODE_OPENXR) {
+ if (hand_tracking > XR_HAND_TRACKING_NONE) {
valid = false;
- err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"OpenXR\".");
+ err += "\n";
+ }
+
+ if (passthrough_mode > XR_PASSTHROUGH_NONE) {
+ valid = false;
+ err += TTR("\"Passthrough\" is only valid when \"Xr Mode\" is \"OpenXR\".");
err += "\n";
}
}
if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB &&
- !bool(p_preset->get("custom_template/use_custom_build"))) {
+ !custom_build_enabled) {
valid = false;
err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled.");
err += "\n";
}
+ // Check the min sdk version
+ int min_sdk_version = p_preset->get("version/min_sdk");
+ if (min_sdk_version != DEFAULT_MIN_SDK_VERSION && !custom_build_enabled) {
+ valid = false;
+ err += TTR("Changing the \"Min Sdk\" is only valid when \"Use Custom Build\" is enabled.");
+ err += "\n";
+ }
+
+ // Check the target sdk version
+ int target_sdk_version = p_preset->get("version/target_sdk");
+ if (target_sdk_version != DEFAULT_TARGET_SDK_VERSION && !custom_build_enabled) {
+ valid = false;
+ err += TTR("Changing the \"Target Sdk\" is only valid when \"Use Custom Build\" is enabled.");
+ err += "\n";
+ }
+
+ if (target_sdk_version < min_sdk_version) {
+ valid = false;
+ err += TTR("\"Target Sdk\" version must be greater or equal to \"Min Sdk\" version.");
+ err += "\n";
+ }
+
r_error = err;
return valid;
}
@@ -2176,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;
}
@@ -2187,7 +2271,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
for (int i = 0; i < command_line_strings.size(); i++) {
if (command_line_strings[i].strip_edges().length() == 0) {
- command_line_strings.remove(i);
+ command_line_strings.remove_at(i);
i--;
}
}
@@ -2207,17 +2291,12 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
}
int xr_mode_index = p_preset->get("xr_features/xr_mode");
- if (xr_mode_index == 1) {
- command_line_strings.push_back("--xr_mode_ovr");
+ if (xr_mode_index == XR_MODE_OPENXR) {
+ command_line_strings.push_back("--xr_mode_openxr");
} else { // XRMode.REGULAR is the default.
command_line_strings.push_back("--xr_mode_regular");
}
- bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer");
- if (use_32_bit_framebuffer) {
- command_line_strings.push_back("--use_depth_32");
- }
-
bool immersive = p_preset->get("screen/immersive_mode");
if (immersive) {
command_line_strings.push_back("--use_immersive");
@@ -2256,7 +2335,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String apksigner = get_apksigner_path();
print_verbose("Starting signing of the " + export_label + " binary using " + apksigner);
if (!FileAccess::exists(apksigner)) {
- EditorNode::add_io_error(vformat(TTR("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting %s is unsigned."), export_label));
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' could not be found. Please check that the command is available in the Android SDK build-tools directory. The resulting %s is unsigned."), export_label));
return OK;
}
@@ -2289,7 +2368,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
if (!FileAccess::exists(keystore)) {
- EditorNode::add_io_error(TTR("Could not find keystore, unable to export."));
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not find keystore, unable to export."));
return ERR_FILE_CANT_OPEN;
}
@@ -2310,10 +2389,14 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
int retval;
output.clear();
- OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ Error err = OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start apksigner executable."));
+ return err;
+ }
print_verbose(output);
if (retval) {
- EditorNode::add_io_error(vformat(TTR("'apksigner' returned with error #%d"), retval));
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' returned with error #%d"), retval));
return ERR_CANT_CREATE;
}
@@ -2330,10 +2413,14 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
output.clear();
- OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ err = OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start apksigner executable."));
+ return err;
+ }
print_verbose(output);
if (retval) {
- EditorNode::add_io_error(vformat(TTR("'apksigner' verification of %s failed."), export_label));
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' verification of %s failed."), export_label));
return ERR_CANT_CREATE;
}
@@ -2342,12 +2429,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);
}
@@ -2355,7 +2442,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);
}
@@ -2375,7 +2462,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]);
@@ -2441,22 +2528,21 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (export_format == EXPORT_FORMAT_AAB) {
if (!p_path.ends_with(".aab")) {
- EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Invalid filename! Android App Bundle requires the *.aab extension."));
return ERR_UNCONFIGURED;
}
if (apk_expansion) {
- EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("APK Expansion not compatible with Android App Bundle."));
return ERR_UNCONFIGURED;
}
}
if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) {
- EditorNode::get_singleton()->show_warning(
- TTR("Invalid filename! Android APK requires the *.apk extension."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Invalid filename! Android APK requires the *.apk extension."));
return ERR_UNCONFIGURED;
}
if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) {
- EditorNode::add_io_error(TTR("Unsupported export format!\n"));
- return ERR_UNCONFIGURED; //TODO: is this the right error?
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unsupported export format!"));
+ return ERR_UNCONFIGURED;
}
if (use_custom_build) {
@@ -2464,20 +2550,19 @@ 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) {
- 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."));
+ Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+ if (f.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), 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));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Android build version mismatch: Template installed: %s, Godot version: %s. Please reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
return ERR_UNCONFIGURED;
}
}
- const String assets_directory = get_assets_directory(p_preset);
+ const String assets_directory = get_assets_directory(p_preset, export_format);
String sdk_path = EDITOR_GET("export/android/android_sdk_path");
ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
print_verbose("Android sdk path: " + sdk_path);
@@ -2486,7 +2571,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String project_name = get_project_name(p_preset->get("package/name"));
err = _create_project_name_strings_files(p_preset, project_name); //project name localization.
if (err != OK) {
- EditorNode::add_io_error(TTR("Unable to overwrite res://android/build/res/*.xml files with project name"));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res://android/build/res/*.xml files with project name."));
}
// Copies the project icon files into the appropriate Gradle project directory.
_copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background);
@@ -2501,22 +2586,21 @@ 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"));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project."));
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!"));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not write expansion package file!"));
return err;
}
}
@@ -2539,6 +2623,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String package_name = get_package_name(p_preset->get("package/unique_name"));
String version_code = itos(p_preset->get("version/code"));
String version_name = p_preset->get("version/name");
+ String min_sdk_version = itos(p_preset->get("version/min_sdk"));
+ String target_sdk_version = itos(p_preset->get("version/target_sdk"));
String enabled_abi_string = String("|").join(enabled_abis);
String sign_flag = should_sign ? "true" : "false";
String zipalign_flag = "true";
@@ -2568,6 +2654,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code.
cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name.
+ cmdline.push_back("-Pexport_version_min_sdk=" + min_sdk_version); // argument to specify the min sdk.
+ cmdline.push_back("-Pexport_version_target_sdk=" + target_sdk_version); // argument to specify the target sdk.
cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs.
cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
@@ -2593,6 +2681,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)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), 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.
@@ -2602,8 +2697,11 @@ 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."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not find keystore, unable to export."));
return ERR_FILE_CANT_OPEN;
}
@@ -2615,7 +2713,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
if (result != 0) {
- EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error. Alternatively visit docs.godotengine.org for Android build documentation."));
return ERR_CANT_CREATE;
}
@@ -2645,7 +2743,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" ")));
int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
if (copy_result != 0) {
- EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
return ERR_CANT_CREATE;
}
@@ -2660,14 +2758,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
src_apk = p_preset->get("custom_template/release");
}
src_apk = src_apk.strip_edges();
- if (src_apk == "") {
+ if (src_apk.is_empty()) {
if (p_debug) {
src_apk = find_export_template("android_debug.apk");
} else {
src_apk = find_export_template("android_release.apk");
}
- if (src_apk == "") {
- EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk));
+ if (src_apk.is_empty()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Package not found: \"%s\"."), src_apk));
return ERR_FILE_NOT_FOUND;
}
}
@@ -2676,8 +2774,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;
@@ -2685,15 +2783,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io);
if (!pkg) {
- EditorNode::add_io_error(vformat(TTR("Could not find template APK to export:\n%s"), src_apk));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not find template APK to export: \"%s\"."), src_apk));
return ERR_FILE_NOT_FOUND;
}
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");
@@ -2701,7 +2798,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);
@@ -2718,10 +2816,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);
@@ -2813,7 +2914,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (!invalid_abis.is_empty()) {
String unsupported_arch = String(", ").join(invalid_abis);
- EditorNode::add_io_error(vformat(TTR("Missing libraries in the export template for the selected architectures: %s.\nPlease build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Missing libraries in the export template for the selected architectures: %s. Please build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch));
CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
}
@@ -2826,25 +2927,25 @@ 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!"));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not write expansion package file!"));
return err;
}
} else {
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);
}
}
if (err != OK) {
unzClose(pkg);
- EditorNode::add_io_error(TTR("Could not export project files"));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not export project files.")));
CLEANUP_AND_RETURN(ERR_SKIP);
}
@@ -2880,15 +2981,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io);
if (!tmp_unaligned) {
- EditorNode::add_io_error(TTR("Could not unzip temporary unaligned APK."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not unzip temporary unaligned APK.")));
CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
}
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
@@ -2902,8 +3001,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);
@@ -2965,7 +3067,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 d33f616f11..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">
@@ -104,7 +89,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String get_package_name(const String &p_package) const;
- String get_assets_directory(const Ref<EditorExportPreset> &p_preset) const;
+ String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
@@ -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 ba7b8ce6c7..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,21 +71,19 @@ 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;
}
bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) {
bool valid_name = !plugin_config.name.is_empty();
bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ||
- plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
+ plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
bool valid_binary = false;
if (valid_binary_type) {
valid_binary = !plugin_config.binary.is_empty() &&
- (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE ||
-
- FileAccess::exists(plugin_config.binary));
+ (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE ||
+ FileAccess::exists(plugin_config.binary));
}
bool valid_local_dependencies = true;
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 851bd0ac52..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,32 +120,48 @@ 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);
return err;
}
+String _android_xml_escape(const String &p_string) {
+ // Android XML requires strings to be both valid XML (`xml_escape()`) but also
+ // to escape characters which are valid XML but have special meaning in Android XML.
+ // https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
+ // Note: Didn't handle U+XXXX unicode chars, could be done if needed.
+ return p_string
+ .replace("@", "\\@")
+ .replace("?", "\\?")
+ .replace("'", "\\'")
+ .replace("\"", "\\\"")
+ .replace("\n", "\\n")
+ .replace("\t", "\\t")
+ .xml_escape(false);
+}
+
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
- String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true));
+ String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(project_name));
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 == "") {
+ if (file.is_empty()) {
break;
}
if (!file.begins_with("values-")) {
@@ -154,11 +169,10 @@ 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);
- String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true));
+ 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);
} else {
@@ -176,7 +190,7 @@ String bool_to_string(bool v) {
String _get_gles_tag() {
bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" &&
- !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2");
+ !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2");
return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : "";
}
@@ -196,41 +210,41 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
String manifest_xr_features;
- bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
+ int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
+ bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
if (uses_xr) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n";
int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
- if (hand_tracking_index == 1) {
+ if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n";
- } else if (hand_tracking_index == 2) {
+ } else if (hand_tracking_index == XR_HAND_TRACKING_REQUIRED) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n";
}
+
+ int passthrough_mode = p_preset->get("xr_features/passthrough");
+ if (passthrough_mode == XR_PASSTHROUGH_OPTIONAL) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"false\" />\n";
+ } else if (passthrough_mode == XR_PASSTHROUGH_REQUIRED) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n";
+ }
}
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) {
- bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
+ 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:screenOrientation=\"%s\">\n",
- orientation);
+ "tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
+ "android:excludeFromRecents=\"%s\" "
+ "android:screenOrientation=\"%s\" "
+ "android:resizeableActivity=\"%s\">\n",
+ bool_to_string(p_preset->get("package/exclude_from_recents")),
+ 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 {
@@ -241,6 +255,8 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
}
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission) {
+ int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
+ bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
String manifest_application_text = vformat(
" <application android:label=\"@string/godot_project_name_string\"\n"
" android:allowBackup=\"%s\"\n"
@@ -249,12 +265,27 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
" android:hasFragileUserData=\"%s\"\n"
" android:requestLegacyExternalStorage=\"%s\"\n"
" tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
- " tools:ignore=\"GoogleAppIndexingWarning\">\n\n",
+ " tools:ignore=\"GoogleAppIndexingWarning\">\n\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")),
bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
bool_to_string(p_has_storage_permission));
+ if (uses_xr) {
+ 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");
+ String hand_tracking_frequency = hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH";
+ 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";
return manifest_application_text;
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 744022f1f9..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 */
@@ -44,6 +44,25 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut
</resources>
)";
+// Supported XR modes.
+// This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java'
+static const int XR_MODE_REGULAR = 0;
+static const int XR_MODE_OPENXR = 1;
+
+// Supported XR hand tracking modes.
+static const int XR_HAND_TRACKING_NONE = 0;
+static const int XR_HAND_TRACKING_OPTIONAL = 1;
+static const int XR_HAND_TRACKING_REQUIRED = 2;
+
+// Supported XR hand tracking frequencies.
+static const int XR_HAND_TRACKING_FREQUENCY_LOW = 0;
+static const int XR_HAND_TRACKING_FREQUENCY_HIGH = 1;
+
+// Supported XR passthrough modes.
+static const int XR_PASSTHROUGH_NONE = 0;
+static const int XR_PASSTHROUGH_OPTIONAL = 1;
+static const int XR_PASSTHROUGH_REQUIRED = 2;
+
struct CustomExportData {
String assets_directory;
bool debug;
@@ -83,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 d7bf6cef30..2d4c4763a2 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -33,11 +33,33 @@
<!-- 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 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. -->
+ <meta-data
+ 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"
android:theme="@style/GodotAppSplashTheme"
android:launchMode="singleTask"
+ android:excludeFromRecents="false"
+ android:exported="true"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:resizeableActivity="false"
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index a391a3ca9a..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 {
@@ -91,8 +105,10 @@ android {
applicationId getExportPackageName()
versionCode getExportVersionCode()
versionName getExportVersionName()
- minSdkVersion versions.minSdk
- targetSdkVersion versions.targetSdk
+ 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 fcee54e493..3daf628e63 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,20 +1,21 @@
ext.versions = [
- androidGradlePlugin: '4.2.2',
- compileSdk : 30,
- minSdk : 19,
- targetSdk : 30,
+ androidGradlePlugin: '7.0.3',
+ compileSdk : 31,
+ minSdk : 19, // Also update 'platform/android/java/lib/AndroidManifest.xml#minSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
+ targetSdk : 31, // 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',
- javaVersion : 1.8,
- ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
+ nexusPublishVersion: '1.1.0',
+ javaVersion : 11,
+ ndkVersion : '23.2.8568313' // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
]
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",
]
@@ -48,11 +49,35 @@ ext.getExportVersionName = { ->
return versionName
}
+ext.getExportMinSdkVersion = { ->
+ String minSdkVersion = project.hasProperty("export_version_min_sdk") ? project.property("export_version_min_sdk") : ""
+ if (minSdkVersion == null || minSdkVersion.isEmpty()) {
+ minSdkVersion = "$versions.minSdk"
+ }
+ try {
+ return Integer.parseInt(minSdkVersion)
+ } catch (NumberFormatException ignored) {
+ return versions.minSdk
+ }
+}
+
+ext.getExportTargetSdkVersion = { ->
+ String targetSdkVersion = project.hasProperty("export_version_target_sdk") ? project.property("export_version_target_sdk") : ""
+ if (targetSdkVersion == null || targetSdkVersion.isEmpty()) {
+ targetSdkVersion = "$versions.targetSdk"
+ }
+ try {
+ return Integer.parseInt(targetSdkVersion)
+ } catch (NumberFormatException ignored) {
+ return versions.targetSdk
+ }
+}
+
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.
@@ -62,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()
@@ -86,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 87bb2ea218..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,12 +209,61 @@ 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.
*/
-task generateGodotTemplates(type: GradleBuild) {
- startParameter.excludedTaskNames = templateExcludedBuildTask()
- tasks = templateBuildTasks()
+task generateGodotTemplates {
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+ dependsOn = templateBuildTasks()
finalizedBy 'zipCustomBuild'
}
@@ -168,18 +271,38 @@ task generateGodotTemplates(type: GradleBuild) {
/**
* Generates the same output as generateGodotTemplates but with dev symbols
*/
-task generateDevTemplate (type: GradleBuild) {
+task generateDevTemplate {
// add parameter to set symbols to true
- startParameter.projectProperties += [doNotStrip: true]
+ gradle.startParameter.projectProperties += [doNotStrip: true]
- startParameter.excludedTaskNames = templateExcludedBuildTask()
- tasks = templateBuildTasks()
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+ dependsOn = templateBuildTasks()
finalizedBy 'zipCustomBuild'
}
/**
- * 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..93cbb47400
--- /dev/null
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -0,0 +1,73 @@
+<?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:exported="false"
+ 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:exported="false"
+ 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.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
new file mode 100644
index 0000000000..a1ade722e8
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* GodotEditor.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+package org.godotengine.editor
+
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.Debug
+import androidx.window.layout.WindowMetricsCalculator
+import org.godotengine.godot.FullScreenGodotApp
+import org.godotengine.godot.utils.PermissionsUtil
+import java.util.*
+import kotlin.math.min
+
+/**
+ * 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.
+ */
+open class GodotEditor : FullScreenGodotApp() {
+
+ companion object {
+ private const val WAIT_FOR_DEBUGGER = false
+
+ private const val COMMAND_LINE_PARAMS = "command_line_params"
+
+ private const val EDITOR_ARG = "--editor"
+ private const val PROJECT_MANAGER_ARG = "--project-manager"
+ }
+
+ private val commandLineParams = ArrayList<String>()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ PermissionsUtil.requestManifestPermissions(this)
+
+ val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS)
+ updateCommandLineParams(params)
+
+ if (BuildConfig.BUILD_TYPE == "debug" && WAIT_FOR_DEBUGGER) {
+ Debug.waitForDebugger()
+ }
+
+ super.onCreate(savedInstanceState)
+ }
+
+ private fun updateCommandLineParams(args: Array<String>?) {
+ // Update the list of command line params with the new args
+ commandLineParams.clear()
+ if (args != null && args.isNotEmpty()) {
+ commandLineParams.addAll(listOf(*args))
+ }
+ }
+
+ override fun getCommandLine() = commandLineParams
+
+ override fun onNewGodotInstanceRequested(args: Array<String>) {
+ // Parse the arguments to figure out which activity to start.
+ var targetClass: Class<*> = GodotGame::class.java
+
+ // Whether we should launch the new godot instance in an adjacent window
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
+ var launchAdjacent =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && (isInMultiWindowMode || isLargeScreen)
+
+ for (arg in args) {
+ if (EDITOR_ARG == arg) {
+ targetClass = GodotEditor::class.java
+ launchAdjacent = false
+ break
+ }
+
+ if (PROJECT_MANAGER_ARG == arg) {
+ targetClass = GodotProjectManager::class.java
+ launchAdjacent = false
+ break
+ }
+ }
+
+ // Launch a new activity
+ val newInstance = 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)
+ }
+
+ // Get the screen's density scale
+ protected val isLargeScreen: Boolean
+ // Get the minimum window size // Correspond to the EXPANDED window size class.
+ get() {
+ val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
+
+ // Get the screen's density scale
+ val scale = resources.displayMetrics.density
+
+ // Get the minimum window size
+ val minSize = min(metrics.bounds.width(), metrics.bounds.height()).toFloat()
+ val minSizeDp = minSize / scale
+ return minSizeDp >= 840f // Correspond to the EXPANDED window size class.
+ }
+
+ override fun setRequestedOrientation(requestedOrientation: Int) {
+ if (!overrideOrientationRequest()) {
+ super.setRequestedOrientation(requestedOrientation)
+ }
+ }
+
+ /**
+ * The Godot Android Editor sets its own orientation via its AndroidManifest
+ */
+ protected open fun overrideOrientationRequest() = 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.kt
index 7f5fd8627c..783095f93a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* GodotInstrumentation.java */
+/* GodotGame.kt */
/*************************************************************************/
/* 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,11 @@
/* 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.
+ */
+class GodotGame : GodotEditor() {
+ override fun overrideOrientationRequest() = false
}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt
new file mode 100644
index 0000000000..bcf4659603
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* GodotProjectManager.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+package org.godotengine.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
+ * [GodotEditor] activity.
+ */
+class GodotProjectManager : 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/gradle/wrapper/gradle-wrapper.jar b/platform/android/java/gradle/wrapper/gradle-wrapper.jar
index f6b961fd5a..e708b1c023 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.jar
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
index 74c5636f8a..ffed3a254e 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Wed Jun 23 23:42:22 PDT 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/platform/android/java/gradlew b/platform/android/java/gradlew
index cccdd3d517..4f906e0c81 100755
--- a/platform/android/java/gradlew
+++ b/platform/android/java/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
-APP_ARGS=$(save "$@")
+APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
exec "$JAVACMD" "$@"
diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat
index 11cc30edb0..107acd32c4 100644
--- a/platform/android/java/gradlew.bat
+++ b/platform/android/java/gradlew.bat
@@ -1,7 +1,23 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
-@rem Gradle startup script for Windows
+@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
@@ -75,7 +80,7 @@ if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index 3034794d69..228d8d45fa 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -4,6 +4,9 @@
android:versionCode="1"
android:versionName="1.0">
+ <!-- Should match the mindSdk and targetSdk values in platform/android/java/app/config.gradle -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
+
<application>
<!-- Records the version of the Godot library -->
@@ -13,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 70bc73b9ad..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;
@@ -119,7 +119,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private Button mWiFiSettingsButton;
private XRMode xrMode = XRMode.REGULAR;
- private boolean use_32_bits = false;
private boolean use_immersive = false;
private boolean use_debug_opengl = false;
private boolean mStatePaused;
@@ -167,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);
@@ -263,11 +263,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
GodotLib.setup(command_line);
final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name");
- if (videoDriver.equals("Vulkan")) {
+ if (videoDriver.equals("vulkan")) {
mRenderView = new GodotVulkanRenderView(activity, this);
} else {
- mRenderView = new GodotGLRenderView(activity, this, xrMode, use_32_bits,
- use_debug_opengl);
+ mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
}
View view = mRenderView.getView();
@@ -335,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) {
@@ -459,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);
@@ -504,25 +502,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
boolean has_extra = i < command_line.length - 1;
if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) {
xrMode = XRMode.REGULAR;
- } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) {
- xrMode = XRMode.OVR;
- } else if (command_line[i].equals("--use_depth_32")) {
- use_32_bits = true;
+ } else if (command_line[i].equals(XRMode.OPENXR.cmdLineArg)) {
+ xrMode = XRMode.OPENXR;
} else if (command_line[i].equals("--debug_opengl")) {
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")) {
@@ -578,8 +571,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
if (!pack_valid) {
Intent notifierIntent = new Intent(activity, activity.getClass());
- notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -665,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) {
@@ -699,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 |
@@ -719,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;
+ }
}
}
@@ -827,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) {
@@ -853,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;
}
@@ -978,6 +1005,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
progress.mOverallTotal));
}
+
public void initInputDevices() {
mRenderView.initInputDevices();
}
@@ -986,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 d33faab641..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 */
@@ -50,9 +50,9 @@ public class GodotDownloaderService extends DownloaderService {
};
/**
- * This public key comes from your Android Market publisher account, and it
- * used by the LVL to validate responses from Market on your behalf.
- */
+ * This public key comes from your Android Market publisher account, and it
+ * used by the LVL to validate responses from Market on your behalf.
+ */
@Override
public String getPublicKey() {
SharedPreferences prefs = getApplicationContext().getSharedPreferences("app_data_keys", Context.MODE_PRIVATE);
@@ -63,20 +63,20 @@ public class GodotDownloaderService extends DownloaderService {
}
/**
- * This is used by the preference obfuscater to make sure that your
- * obfuscated preferences are different than the ones used by other
- * applications.
- */
+ * This is used by the preference obfuscater to make sure that your
+ * obfuscated preferences are different than the ones used by other
+ * applications.
+ */
@Override
public byte[] getSALT() {
return SALT;
}
/**
- * Fill this in with the class name for your alarm receiver. We do this
- * because receivers must be unique across all of Android (it's a good idea
- * to make sure that your receiver is in your unique package)
- */
+ * Fill this in with the class name for your alarm receiver. We do this
+ * because receivers must be unique across all of Android (it's a good idea
+ * to make sure that your receiver is in your unique package)
+ */
@Override
public String getAlarmReceiverClassName() {
Log.d("GODOT", "getAlarmReceiverClassName()");
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 a9d45c943b..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;
@@ -78,10 +79,8 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
private final GodotRenderer godotRenderer;
private PointerIcon pointerIcon;
- public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
- boolean p_use_debug_opengl) {
+ public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) {
super(context);
- GLUtils.use_32 = p_use_32_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl;
this.godot = godot;
@@ -91,7 +90,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
}
- init(xrMode, false, 16, 0);
+ init(xrMode, false);
}
@Override
@@ -172,11 +171,11 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return pointerIcon;
}
- private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
+ private void init(XRMode xrMode, boolean translucent) {
setPreserveEGLContextOnPause(true);
setFocusableInTouchMode(true);
switch (xrMode) {
- case OVR:
+ case OPENXR:
// Replace the default egl config chooser.
setEGLConfigChooser(new OvrConfigChooser());
@@ -209,18 +208,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
* below.
*/
- if (GLUtils.use_32) {
- setEGLConfigChooser(translucent ?
- new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
- new RegularConfigChooser(8, 8, 8, 8, 16, stencil)) :
- new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
- new RegularConfigChooser(5, 6, 5, 0, 16, stencil)));
-
- } else {
- setEGLConfigChooser(translucent ?
- new RegularConfigChooser(8, 8, 8, 8, 16, stencil) :
- new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
- }
+ setEGLConfigChooser(
+ new RegularFallbackConfigChooser(8, 8, 8, 8, 24, 0,
+ new RegularConfigChooser(8, 8, 8, 8, 16, 0)));
break;
}
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 d85d88ec6c..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);
@@ -288,7 +339,34 @@ public class GodotIO {
}
public int getScreenOrientation() {
- return activity.getRequestedOrientation();
+ int orientation = activity.getRequestedOrientation();
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ return SCREEN_LANDSCAPE;
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ return SCREEN_PORTRAIT;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ return SCREEN_REVERSE_LANDSCAPE;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ return SCREEN_REVERSE_PORTRAIT;
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ return SCREEN_SENSOR_LANDSCAPE;
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ return SCREEN_SENSOR_PORTRAIT;
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR:
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR:
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ return SCREEN_SENSOR;
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_BEHIND:
+ case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR:
+ case ActivityInfo.SCREEN_ORIENTATION_LOCKED:
+ default:
+ return -1;
+ }
}
public void setEdit(GodotEditText _edit) {
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 95870acda1..3182ab0666 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,16 +70,15 @@ 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);
/**
* Invoked on the render thread when the underlying Android surface is created or recreated.
* @param p_surface
- * @param p_32_bits
*/
- public static native void newcontext(Surface p_surface, boolean p_32_bits);
+ public static native void newcontext(Surface p_surface);
/**
* Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
@@ -86,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.
@@ -108,11 +114,6 @@ public class GodotLib {
public static native void doubleTap(int buttonMask, int x, int y);
/**
- * Forward scroll events from the main thread to the GL thread.
- */
- public static native void scroll(int x, int y);
-
- /**
* Forward accelerometer sensor events from the main thread to the GL thread.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
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 878a119c5c..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) {
@@ -70,19 +70,19 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- GodotLib.newcontext(null, GLUtils.use_32);
+ GodotLib.newcontext(null);
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLSurfaceCreated(gl, config);
}
}
- 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 d1e8ae5ca9..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 */
@@ -191,9 +191,9 @@ public class GodotEditText extends EditText {
private boolean needHandlingInGodot(int keyCode, KeyEvent keyEvent) {
boolean isArrowKey = keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
- keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT;
+ keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT;
boolean isModifiedKey = keyEvent.isAltPressed() || keyEvent.isCtrlPressed() || keyEvent.isSymPressed() ||
- keyEvent.isFunctionPressed() || keyEvent.isMetaPressed();
+ keyEvent.isFunctionPressed() || keyEvent.isMetaPressed();
return isArrowKey || keyCode == KeyEvent.KEYCODE_TAB || KeyEvent.isModifierKey(keyCode) ||
isModifiedKey;
}
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..778efa914a 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 */
@@ -80,15 +80,6 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener
}
@Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- //Log.i("GodotGesture", "onScroll");
- final int x = Math.round(distanceX);
- final int y = Math.round(distanceY);
- GodotLib.scroll(x, y);
- return true;
- }
-
- @Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
//Log.i("GodotGesture", "onFling");
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index fc0b84b392..ccfb865b1a 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);
}
@@ -185,6 +186,9 @@ public class GodotInputHandler implements InputDeviceListener {
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
final int godotJoyId = mJoystickIds.get(deviceId);
Joystick joystick = mJoysticksDevices.get(deviceId);
+ if (joystick == null) {
+ return true;
+ }
for (int i = 0; i < joystick.axes.size(); i++) {
final int axis = joystick.axes.get(i);
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 19588f8465..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 */
@@ -44,7 +44,6 @@ public class GLUtils {
public static final boolean DEBUG = false;
- public static boolean use_32 = false;
public static boolean use_debug_opengl = false;
private static final String[] ATTRIBUTES_NAMES = new String[] {
@@ -148,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 a35f6ec5a7..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 */
@@ -58,7 +58,7 @@ internal class VkRenderer {
* Called when the surface is created and signals the beginning of rendering.
*/
fun onVkSurfaceCreated(surface: Surface) {
- GodotLib.newcontext(surface, false)
+ GodotLib.newcontext(surface)
for (plugin in pluginRegistry.getAllPlugins()) {
plugin.onVkSurfaceCreated(surface)
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 0995477baf..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 */
@@ -35,7 +35,7 @@ package org.godotengine.godot.xr;
*/
public enum XRMode {
REGULAR(0, "Regular", "--xr_mode_regular", "Default Android Gamepad"), // Regular/flatscreen
- OVR(1, "Oculus Mobile VR", "--xr_mode_ovr", "");
+ OPENXR(1, "OpenXR", "--xr_mode_openxr", "");
final int index;
final String label;
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 e690c5b695..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,15 +30,13 @@
package org.godotengine.godot.xr.regular;
-import org.godotengine.godot.utils.GLUtils;
-
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
-/* Fallback if 32bit View is not supported*/
+/* Fallback if the requested configuration is not supported */
public class RegularFallbackConfigChooser extends RegularConfigChooser {
private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName();
@@ -55,7 +53,6 @@ public class RegularFallbackConfigChooser extends RegularConfigChooser {
if (ec == null) {
Log.w(TAG, "Trying ConfigChooser fallback");
ec = fallback.chooseConfig(egl, display, configs);
- GLUtils.use_32 = false;
}
return ec;
}
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 d971727269..eaffe14b13 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) {
@@ -182,11 +182,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface) {
if (os_android) {
if (step.get() == 0) {
// During startup
- os_android->set_context_is_16_bits(!p_32_bits);
if (p_surface) {
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
os_android->set_native_window(native_window);
@@ -201,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,
@@ -219,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);
@@ -238,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++) {
@@ -280,32 +289,27 @@ 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)
- 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;
@@ -318,8 +322,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
@@ -332,24 +337,27 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) {
- if (step.get() <= 0)
+ if (step.get() <= 0) {
return;
+ }
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = AndroidInputHandler::JOY_EVENT_HAT;
- int hat = 0;
+ HatMask hat = HatMask::CENTER;
if (p_hat_x != 0) {
- if (p_hat_x < 0)
- hat |= HatMask::HAT_MASK_LEFT;
- else
- hat |= HatMask::HAT_MASK_RIGHT;
+ if (p_hat_x < 0) {
+ hat |= HatMask::LEFT;
+ } else {
+ hat |= HatMask::RIGHT;
+ }
}
if (p_hat_y != 0) {
- if (p_hat_y < 0)
- hat |= HatMask::HAT_MASK_UP;
- else
- hat |= HatMask::HAT_MASK_DOWN;
+ if (p_hat_y < 0) {
+ hat |= HatMask::UP;
+ } else {
+ hat |= HatMask::DOWN;
+ }
}
jevent.hat = hat;
@@ -366,8 +374,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);
}
@@ -389,15 +398,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();
}
@@ -410,7 +421,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);
@@ -421,26 +432,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);
@@ -448,18 +459,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);
}
@@ -475,17 +489,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 63e9e6d8e5..aa8d67cf46 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 */
@@ -41,8 +41,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT 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, jboolean p_32_bits);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
@@ -50,7 +51,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEn
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
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);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
@@ -71,4 +71,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 21fb31d991..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,12 +224,16 @@ 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 {
String locale = godot_io_java->get_locale();
- if (locale != "") {
+ if (!locale.is_empty()) {
return locale;
}
@@ -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 != "")
+ if (!model.is_empty()) {
return model;
+ }
return OS_Unix::get_model_name();
}
@@ -217,12 +253,21 @@ 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 != String())
+ if (!data_dir_cache.is_empty()) {
return data_dir_cache;
+ }
String data_dir = godot_io_java->get_user_data_dir();
- if (data_dir != "") {
+ if (!data_dir.is_empty()) {
data_dir_cache = _remove_symlink(data_dir);
return data_dir_cache;
}
@@ -230,18 +275,23 @@ String OS_Android::get_user_data_dir() const {
}
String OS_Android::get_cache_path() const {
+ if (!cache_dir_cache.is_empty()) {
+ return cache_dir_cache;
+ }
+
String cache_dir = godot_io_java->get_cache_dir();
- if (cache_dir != "") {
- cache_dir = _remove_symlink(cache_dir);
- return cache_dir;
+ if (!cache_dir.is_empty()) {
+ cache_dir_cache = _remove_symlink(cache_dir);
+ return cache_dir_cache;
}
return ".";
}
String OS_Android::get_unique_id() const {
String unique_id = godot_io_java->get_unique_id();
- if (unique_id != "")
+ if (!unique_id.is_empty()) {
return unique_id;
+ }
return OS::get_unique_id();
}
@@ -258,17 +308,9 @@ Size2i OS_Android::get_display_size() const {
return display_size;
}
-void OS_Android::set_context_is_16_bits(bool p_is_16) {
-#if defined(OPENGL_ENABLED)
- //use_16bits_fbo = p_is_16;
- //if (rasterizer)
- // rasterizer->set_force_16_bits_fbo(p_is_16);
-#endif
-}
-
void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
-#if defined(OPENGL_ENABLED)
- ERR_FAIL_COND(!p_gl_extensions);
+#if defined(GLES3_ENABLED)
+ ERR_FAIL_NULL(p_gl_extensions);
gl_extensions = p_gl_extensions;
#endif
}
@@ -291,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;
@@ -319,10 +365,9 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
main_loop = nullptr;
-#if defined(OPENGL_ENABLED)
+#if defined(GLES3_ENABLED)
gl_extensions = nullptr;
use_gl2 = false;
- use_16bits_fbo = false;
#endif
#if defined(VULKAN_ENABLED)
@@ -341,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 c938297821..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 */
@@ -47,25 +47,27 @@ private:
bool use_apk_expansion;
-#if defined(OPENGL_ENABLED)
- bool use_16bits_fbo;
+#if defined(GLES3_ENABLED)
const char *gl_extensions;
#endif
#if defined(VULKAN_ENABLED)
- ANativeWindow *native_window;
+ ANativeWindow *native_window = nullptr;
#endif
mutable String data_dir_cache;
+ mutable String cache_dir_cache;
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();
@@ -102,13 +104,13 @@ public:
void set_display_size(const Size2i &p_size);
Size2i get_display_size() const;
- void set_context_is_16_bits(bool p_is_16);
void set_opengl_extensions(const char *p_gl_extensions);
void set_native_window(ANativeWindow *p_native_window);
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;
@@ -122,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 */