summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/android_input_handler.cpp81
-rw-r--r--platform/android/android_input_handler.h10
-rw-r--r--platform/android/android_keys_utils.cpp38
-rw-r--r--platform/android/android_keys_utils.h3
-rw-r--r--platform/android/audio_driver_opensl.cpp14
-rw-r--r--platform/android/audio_driver_opensl.h27
-rw-r--r--platform/android/display_server_android.cpp15
-rw-r--r--platform/android/export/export_plugin.cpp148
-rw-r--r--platform/android/export/export_plugin.h10
-rw-r--r--platform/android/export/gradle_export_util.cpp45
-rw-r--r--platform/android/export/gradle_export_util.h6
-rw-r--r--platform/android/file_access_android.cpp4
-rw-r--r--platform/android/file_access_android.h2
-rw-r--r--platform/android/file_access_filesystem_jandroid.cpp6
-rw-r--r--platform/android/file_access_filesystem_jandroid.h2
-rw-r--r--platform/android/java/app/AndroidManifest.xml30
-rw-r--r--platform/android/java/app/build.gradle8
-rw-r--r--platform/android/java/app/gradle.properties4
-rw-r--r--platform/android/java/app/settings.gradle2
-rw-r--r--platform/android/java/app/src/com/godot/game/GodotApp.java2
-rw-r--r--platform/android/java/build.gradle14
-rw-r--r--platform/android/java/editor/src/dev/res/values/strings.xml2
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt50
-rw-r--r--platform/android/java/editor/src/main/res/values/dimens.xml2
-rw-r--r--platform/android/java/editor/src/main/res/values/strings.xml2
-rw-r--r--platform/android/java/gradle.properties2
-rw-r--r--platform/android/java/lib/AndroidManifest.xml10
-rw-r--r--platform/android/java/lib/res/values/strings.xml3
-rw-r--r--platform/android/java/lib/res/xml/godot_provider_paths.xml11
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java30
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java74
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java22
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java39
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java22
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java2
-rw-r--r--platform/android/java_godot_lib_jni.cpp6
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/android/java_godot_wrapper.cpp19
-rw-r--r--platform/android/java_godot_wrapper.h4
-rw-r--r--platform/android/os_android.cpp16
-rw-r--r--platform/android/os_android.h3
44 files changed, 480 insertions, 328 deletions
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index e3f93086dc..63045237e9 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -49,14 +49,22 @@ void AndroidInputHandler::process_joy_event(AndroidInputHandler::JoypadEvent p_e
}
}
-void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> ev) {
- ev->set_shift_pressed(shift_mem);
- ev->set_alt_pressed(alt_mem);
- ev->set_meta_pressed(meta_mem);
- ev->set_ctrl_pressed(control_mem);
+void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> ev, Key p_keycode) {
+ if (p_keycode != Key::SHIFT) {
+ ev->set_shift_pressed(shift_mem);
+ }
+ if (p_keycode != Key::ALT) {
+ ev->set_alt_pressed(alt_mem);
+ }
+ if (p_keycode != Key::META) {
+ ev->set_meta_pressed(meta_mem);
+ }
+ if (p_keycode != Key::CTRL) {
+ ev->set_ctrl_pressed(control_mem);
+ }
}
-void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed) {
+void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed) {
static char32_t prev_wc = 0;
char32_t unicode = p_unicode;
if ((p_unicode & 0xfffffc00) == 0xd800) {
@@ -81,8 +89,18 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Key physical_keycode = godot_code_from_android_code(p_physical_keycode);
Key keycode = physical_keycode;
- if (p_keycode != 0) {
- keycode = godot_code_from_unicode(p_keycode);
+ if (unicode == '\b') { // 0x08
+ keycode = Key::BACKSPACE;
+ } else if (unicode == '\t') { // 0x09
+ keycode = Key::TAB;
+ } else if (unicode == '\n') { // 0x0A
+ keycode = Key::ENTER;
+ } else if (unicode == 0x1B) {
+ keycode = Key::ESCAPE;
+ } else if (unicode == 0x1F) {
+ keycode = Key::KEY_DELETE;
+ } else {
+ keycode = fix_keycode(unicode, physical_keycode);
}
switch (physical_keycode) {
@@ -104,10 +122,11 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
ev->set_keycode(keycode);
ev->set_physical_keycode(physical_keycode);
- ev->set_unicode(unicode);
+ ev->set_key_label(fix_key_label(p_key_label, keycode));
+ ev->set_unicode(fix_unicode(unicode));
ev->set_pressed(p_pressed);
- _set_key_modifier_state(ev);
+ _set_key_modifier_state(ev, keycode);
if (p_physical_keycode == AKEYCODE_BACK) {
if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) {
@@ -118,13 +137,22 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Input::get_singleton()->parse_input_event(ev);
}
-void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_double_tap) {
+void AndroidInputHandler::_cancel_all_touch() {
+ _parse_all_touch(false, false, true);
+ touch.clear();
+}
+
+void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_double_tap, bool reset_index) {
if (touch.size()) {
//end all if exist
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
ev.instantiate();
- ev->set_index(touch[i].id);
+ if (reset_index) {
+ ev->set_index(-1);
+ } else {
+ ev->set_index(touch[i].id);
+ }
ev->set_pressed(p_pressed);
ev->set_position(touch[i].pos);
ev->set_double_tap(p_double_tap);
@@ -185,7 +213,9 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
}
} break;
- case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ _cancel_all_touch();
+ } break;
case AMOTION_EVENT_ACTION_UP: { //release
_release_all_touch();
} break;
@@ -225,6 +255,12 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
}
}
+void AndroidInputHandler::_cancel_mouse_event_info(bool p_source_mouse_relative) {
+ buttons_state = BitField<MouseButtonMask>();
+ _parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, p_source_mouse_relative);
+ mouse_event_info.valid = false;
+}
+
void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
if (!mouse_event_info.valid) {
return;
@@ -232,7 +268,7 @@ void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> even
Ref<InputEventMouseButton> ev;
ev.instantiate();
- _set_key_modifier_state(ev);
+ _set_key_modifier_state(ev, Key::NONE);
if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
@@ -266,7 +302,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
Ref<InputEventMouseMotion> ev;
ev.instantiate();
- _set_key_modifier_state(ev);
+ _set_key_modifier_state(ev, Key::NONE);
ev->set_position(p_event_pos);
ev->set_global_position(p_event_pos);
ev->set_relative(p_event_pos - hover_prev_pos);
@@ -285,8 +321,11 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
_parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative);
} break;
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ _cancel_mouse_event_info(p_source_mouse_relative);
+ } break;
+
case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
_release_mouse_event_info(p_source_mouse_relative);
} break;
@@ -298,7 +337,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
Ref<InputEventMouseMotion> ev;
ev.instantiate();
- _set_key_modifier_state(ev);
+ _set_key_modifier_state(ev, Key::NONE);
if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
@@ -317,7 +356,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev;
ev.instantiate();
- _set_key_modifier_state(ev);
+ _set_key_modifier_state(ev, Key::NONE);
if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
@@ -344,7 +383,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
Ref<InputEventMouseButton> evd = ev->duplicate();
- _set_key_modifier_state(evd);
+ _set_key_modifier_state(evd, Key::NONE);
evd->set_button_index(wheel_button);
evd->set_button_mask(BitField<MouseButtonMask>(event_buttons_mask.operator int64_t() ^ int64_t(mouse_button_to_mask(wheel_button))));
evd->set_factor(factor);
@@ -358,7 +397,7 @@ void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_bu
void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
Ref<InputEventMagnifyGesture> magnify_event;
magnify_event.instantiate();
- _set_key_modifier_state(magnify_event);
+ _set_key_modifier_state(magnify_event, Key::NONE);
magnify_event->set_position(p_pos);
magnify_event->set_factor(p_factor);
Input::get_singleton()->parse_input_event(magnify_event);
@@ -367,7 +406,7 @@ void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
Ref<InputEventPanGesture> pan_event;
pan_event.instantiate();
- _set_key_modifier_state(pan_event);
+ _set_key_modifier_state(pan_event, Key::NONE);
pan_event->set_position(p_pos);
pan_event->set_delta(p_delta);
Input::get_singleton()->parse_input_event(pan_event);
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 34259efd81..2badd32636 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -76,7 +76,7 @@ private:
MouseEventInfo mouse_event_info;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
- void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
+ void _set_key_modifier_state(Ref<InputEventWithModifiers> ev, Key p_keycode);
static MouseButton _button_index_from_mask(BitField<MouseButtonMask> button_mask);
static BitField<MouseButtonMask> _android_button_mask_to_godot_button_mask(int android_button_mask);
@@ -87,17 +87,21 @@ private:
void _release_mouse_event_info(bool p_source_mouse_relative = false);
- void _parse_all_touch(bool p_pressed, bool p_double_tap);
+ void _cancel_mouse_event_info(bool p_source_mouse_relative = false);
+
+ void _parse_all_touch(bool p_pressed, bool p_double_tap, bool reset_index = false);
void _release_all_touch();
+ void _cancel_all_touch();
+
public:
void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap);
void process_magnify(Point2 p_pos, float p_factor);
void process_pan(Point2 p_pos, Vector2 p_delta);
void process_joy_event(JoypadEvent p_event);
- void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
+ void process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed);
};
#endif // ANDROID_INPUT_HANDLER_H
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index 0dc2d19cdd..f50437e82a 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -38,41 +38,3 @@ Key godot_code_from_android_code(unsigned int p_code) {
}
return Key::UNKNOWN;
}
-
-Key godot_code_from_unicode(unsigned int p_code) {
- unsigned int code = p_code;
- if (code > 0xFF) {
- return Key::UNKNOWN;
- }
- // Known control codes.
- if (code == '\b') { // 0x08
- return Key::BACKSPACE;
- }
- if (code == '\t') { // 0x09
- return Key::TAB;
- }
- if (code == '\n') { // 0x0A
- return Key::ENTER;
- }
- if (code == 0x1B) {
- return Key::ESCAPE;
- }
- if (code == 0x1F) {
- return Key::KEY_DELETE;
- }
- // Unknown control codes.
- if (code <= 0x1F || (code >= 0x80 && code <= 0x9F)) {
- return Key::UNKNOWN;
- }
- // Convert to uppercase.
- if (code >= 'a' && code <= 'z') { // 0x61 - 0x7A
- code -= ('a' - 'A');
- }
- if (code >= u'à' && code <= u'ö') { // 0xE0 - 0xF6
- code -= (u'à' - u'À'); // 0xE0 - 0xC0
- }
- if (code >= u'ø' && code <= u'þ') { // 0xF8 - 0xFF
- code -= (u'ø' - u'Ø'); // 0xF8 - 0xD8
- }
- return Key(code);
-}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index 33e7929278..3a587dd680 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -165,13 +165,14 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
{ AKEYCODE_NUMPAD_DOT, Key::KP_PERIOD }, // (158) Numeric keypad '.' key (for decimals or digit grouping).
{ AKEYCODE_NUMPAD_ENTER, Key::KP_ENTER }, // (160) Numeric keypad Enter key.
{ AKEYCODE_VOLUME_MUTE, Key::VOLUMEMUTE }, // (164) Volume Mute key.
+ { AKEYCODE_EISU, Key::JIS_EISU }, // (212) JIS EISU key.
{ AKEYCODE_YEN, Key::YEN }, // (216) Japanese Yen key.
+ { AKEYCODE_KANA, Key::JIS_KANA }, // (218) JIS KANA key.
{ AKEYCODE_HELP, Key::HELP }, // (259) Help key.
{ AKEYCODE_REFRESH, Key::REFRESH }, // (285) Refresh key.
{ AKEYCODE_MAX, Key::UNKNOWN }
};
Key godot_code_from_android_code(unsigned int p_code);
-Key godot_code_from_unicode(unsigned int p_code);
#endif // ANDROID_KEYS_UTILS_H
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 27cb84fb9d..5fc32132e3 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -44,7 +44,7 @@ void AudioDriverOpenSL::_buffer_callback(
if (pause) {
mix = false;
} else {
- mix = mutex.try_lock() == OK;
+ mix = mutex.try_lock();
}
if (mix) {
@@ -80,10 +80,6 @@ void AudioDriverOpenSL::_buffer_callbacks(
ad->_buffer_callback(queueItf);
}
-const char *AudioDriverOpenSL::get_name() const {
- return "Android";
-}
-
Error AudioDriverOpenSL::init() {
SLresult res;
SLEngineOption EngineOption[] = {
@@ -204,7 +200,7 @@ void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf q
ad->_record_buffer_callback(queueItf);
}
-Error AudioDriverOpenSL::capture_init_device() {
+Error AudioDriverOpenSL::init_input_device() {
SLDataLocator_IODevice loc_dev = {
SL_DATALOCATOR_IODEVICE,
SL_IODEVICE_AUDIOINPUT,
@@ -271,15 +267,15 @@ Error AudioDriverOpenSL::capture_init_device() {
return OK;
}
-Error AudioDriverOpenSL::capture_start() {
+Error AudioDriverOpenSL::input_start() {
if (OS::get_singleton()->request_permission("RECORD_AUDIO")) {
- return capture_init_device();
+ return init_input_device();
}
return OK;
}
-Error AudioDriverOpenSL::capture_stop() {
+Error AudioDriverOpenSL::input_stop() {
SLuint32 state;
SLresult res = (*recordItf)->GetRecordState(recordItf, &state);
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index ae8c33fec0..6ea0f77def 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -84,23 +84,26 @@ class AudioDriverOpenSL : public AudioDriver {
SLAndroidSimpleBufferQueueItf queueItf,
void *pContext);
- virtual Error capture_init_device();
+ Error init_input_device();
public:
- virtual const char *get_name() const;
+ virtual const char *get_name() const override {
+ return "Android";
+ }
- virtual Error init();
- virtual void start();
- virtual int get_mix_rate() const;
- virtual SpeakerMode get_speaker_mode() const;
- virtual void lock();
- virtual void unlock();
- virtual void finish();
+ virtual Error init() override;
+ virtual void start() override;
+ virtual int get_mix_rate() const override;
+ virtual SpeakerMode get_speaker_mode() const override;
- virtual void set_pause(bool p_pause);
+ virtual void lock() override;
+ virtual void unlock() override;
+ virtual void finish() override;
- virtual Error capture_start();
- virtual Error capture_stop();
+ virtual Error input_start() override;
+ virtual Error input_stop() override;
+
+ void set_pause(bool p_pause);
AudioDriverOpenSL();
};
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 6b3bdb7fe6..af4ba1255b 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -212,7 +212,18 @@ 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 screen_scale = godot_io_java->get_scaled_density();
+
+ // Update the scale to avoid cropping.
+ Size2i screen_size = screen_get_size(p_screen);
+ if (screen_size != Size2i()) {
+ float width_scale = screen_size.width / (float)OS_Android::DEFAULT_WINDOW_WIDTH;
+ float height_scale = screen_size.height / (float)OS_Android::DEFAULT_WINDOW_HEIGHT;
+ screen_scale = MIN(screen_scale, MIN(width_scale, height_scale));
+ }
+
+ print_line("Selected screen scale: ", screen_scale);
+ return screen_scale;
}
float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
@@ -645,6 +656,7 @@ void DisplayServerAndroid::_cursor_set_shape_helper(CursorShape p_shape, bool fo
}
void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) {
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
_cursor_set_shape_helper(p_shape);
}
@@ -653,6 +665,7 @@ DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const {
}
void DisplayServerAndroid::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
String cursor_path = p_cursor.is_valid() ? p_cursor->get_path() : "";
if (!cursor_path.is_empty()) {
cursor_path = ProjectSettings::get_singleton()->globalize_path(cursor_path);
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 587caf81bf..641258a26c 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -251,7 +251,8 @@ 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 = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
+static const int OPENGL_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
+static const int VULKAN_MIN_SDK_VERSION = 24;
static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
#ifndef ANDROID_ENABLED
@@ -799,6 +800,12 @@ bool EditorExportPlatformAndroid::_has_manage_external_storage_permission(const
return p_permissions.find("android.permission.MANAGE_EXTERNAL_STORAGE") != -1;
}
+bool EditorExportPlatformAndroid::_uses_vulkan() {
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ bool uses_vulkan = (current_renderer == "forward_plus" || current_renderer == "mobile") && GLOBAL_GET("rendering/rendering_device/driver.android") == "vulkan";
+ return uses_vulkan;
+}
+
void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
const char **aperms = android_perms;
while (*aperms) {
@@ -853,7 +860,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
}
}
- manifest_text += _get_xr_features_tag(p_preset);
+ manifest_text += _get_xr_features_tag(p_preset, _uses_vulkan());
manifest_text += _get_application_tag(p_preset, _has_read_write_storage_permission(perms));
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
@@ -899,10 +906,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
bool screen_support_large = p_preset->get("screen/support_large");
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");
int app_category = p_preset->get("package/app_category");
bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall");
@@ -1027,6 +1030,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]);
}
+ if (tname == "provider" && attrname == "authorities") {
+ string_table.write[attr_value] = get_package_name(package_name) + String(".fileprovider");
+ }
+
if (tname == "supports-screens") {
if (attrname == "smallScreens") {
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
@@ -1042,25 +1049,6 @@ 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 == "value" && value == "xr_hand_tracking_version_value") {
- string_table.write[attr_value] = "V2.0";
- }
- }
-
iofs += 20;
}
@@ -1075,26 +1063,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
Vector<bool> feature_required_list;
Vector<int> feature_versions;
- if (xr_mode_index == XR_MODE_OPENXR) {
- // Set degrees of freedom
- feature_names.push_back("android.hardware.vr.headtracking");
+ if (_uses_vulkan()) {
+ // Require vulkan hardware level 1 support
+ feature_names.push_back("android.hardware.vulkan.level");
feature_required_list.push_back(true);
feature_versions.push_back(1);
-
- // Check for hand tracking
- if (hand_tracking_index > XR_HAND_TRACKING_NONE) {
- feature_names.push_back("oculus.software.handtracking");
- 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) {
@@ -1716,6 +1689,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enable
void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
r_features->push_back("etc2");
+ r_features->push_back("astc");
Vector<ABI> abis = get_enabled_abis(p_preset);
for (int i = 0; i < abis.size(); ++i) {
@@ -1728,12 +1702,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_build/use_custom_build"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
// This implies doing validation that the string is a proper int.
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", VULKAN_MIN_SDK_VERSION)), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), ""));
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
for (int i = 0; i < plugins_configs.size(); i++) {
@@ -2158,11 +2132,11 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
bool EditorExportPlatformAndroid::has_valid_export_configuration(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_build/use_custom_build");
+ const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
// Look for export templates (first official, and if defined custom templates).
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
String template_err;
bool dvalid = false;
bool rvalid = false;
@@ -2269,7 +2243,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
valid = false;
}
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2293,7 +2267,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
String err;
bool valid = true;
- const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build");
+ const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
// Validate the project configuration.
bool apk_expansion = p_preset->get("apk_expansion/enable");
@@ -2322,11 +2296,11 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += etc_error;
}
- // Ensure that `Use Custom Build` is enabled if a plugin is selected.
+ // Ensure that `Use Gradle Build` is enabled if a plugin is selected.
String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset));
- if (!enabled_plugins_names.is_empty() && !custom_build_enabled) {
+ if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) {
valid = false;
- err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
+ err += TTR("\"Use Gradle Build\" must be enabled to use the plugins.");
err += "\n";
}
@@ -2334,6 +2308,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
int xr_mode_index = p_preset->get("xr_features/xr_mode");
int hand_tracking = p_preset->get("xr_features/hand_tracking");
int passthrough_mode = p_preset->get("xr_features/passthrough");
+ if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) {
+ valid = false;
+ err += TTR("OpenXR requires \"Use Gradle Build\" to be enabled");
+ err += "\n";
+ }
+
if (xr_mode_index != XR_MODE_OPENXR) {
if (hand_tracking > XR_HAND_TRACKING_NONE) {
valid = false;
@@ -2348,20 +2328,20 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
}
- if (int(p_preset->get("custom_build/export_format")) == EXPORT_FORMAT_AAB &&
- !custom_build_enabled) {
+ if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB &&
+ !gradle_build_enabled) {
valid = false;
- err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Export AAB\" is only valid when \"Use Gradle Build\" is enabled.");
err += "\n";
}
// Check the min sdk version.
- String min_sdk_str = p_preset->get("custom_build/min_sdk");
- int min_sdk_int = DEFAULT_MIN_SDK_VERSION;
+ String min_sdk_str = p_preset->get("gradle_build/min_sdk");
+ int min_sdk_int = VULKAN_MIN_SDK_VERSION;
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
valid = false;
- err += TTR("\"Min SDK\" can only be overridden when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Min SDK\" can only be overridden when \"Use Gradle Build\" is enabled.");
err += "\n";
}
if (!min_sdk_str.is_valid_int()) {
@@ -2370,21 +2350,21 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += "\n";
} else {
min_sdk_int = min_sdk_str.to_int();
- if (min_sdk_int < DEFAULT_MIN_SDK_VERSION) {
+ if (min_sdk_int < OPENGL_MIN_SDK_VERSION) {
valid = false;
- err += vformat(TTR("\"Min SDK\" cannot be lower than %d, which is the version needed by the Godot library."), DEFAULT_MIN_SDK_VERSION);
+ err += vformat(TTR("\"Min SDK\" cannot be lower than %d, which is the version needed by the Godot library."), OPENGL_MIN_SDK_VERSION);
err += "\n";
}
}
}
// Check the target sdk version.
- String target_sdk_str = p_preset->get("custom_build/target_sdk");
+ String target_sdk_str = p_preset->get("gradle_build/target_sdk");
int target_sdk_int = DEFAULT_TARGET_SDK_VERSION;
if (!target_sdk_str.is_empty()) { // Empty means no override, nothing to do.
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
valid = false;
- err += TTR("\"Target SDK\" can only be overridden when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Target SDK\" can only be overridden when \"Use Gradle Build\" is enabled.");
err += "\n";
}
if (!target_sdk_str.is_valid_int()) {
@@ -2407,6 +2387,18 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += "\n";
}
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ if (current_renderer == "forward_plus") {
+ // Warning only, so don't override `valid`.
+ err += vformat(TTR("The \"%s\" renderer is designed for Desktop devices, and is not suitable for Android devices."), current_renderer);
+ err += "\n";
+ }
+ if (_uses_vulkan() && min_sdk_int < VULKAN_MIN_SDK_VERSION) {
+ // Warning only, so don't override `valid`.
+ err += vformat(TTR("\"Min SDK\" should be greater or equal to %d for the \"%s\" renderer."), VULKAN_MIN_SDK_VERSION, current_renderer);
+ err += "\n";
+ }
+
r_error = err;
return valid;
}
@@ -2492,12 +2484,12 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
}
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
- int export_format = int(p_preset->get("custom_build/export_format"));
+ int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
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");
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2674,7 +2666,7 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA
}
Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- int export_format = int(p_preset->get("custom_build/export_format"));
+ int export_format = int(p_preset->get("gradle_build/export_format"));
bool should_sign = p_preset->get("package/signed");
return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
}
@@ -2687,7 +2679,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
EditorProgress ep("export", TTR("Exporting for Android"), 105, true);
- bool use_custom_build = bool(p_preset->get("custom_build/use_custom_build"));
+ bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -2697,7 +2689,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
print_verbose("- export path: " + p_path);
print_verbose("- export format: " + itos(export_format));
print_verbose("- sign build: " + bool_to_string(should_sign));
- print_verbose("- custom build enabled: " + bool_to_string(use_custom_build));
+ print_verbose("- gradle build enabled: " + bool_to_string(use_gradle_build));
print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion));
print_verbose("- enabled abis: " + join_abis(enabled_abis, ",", false));
print_verbose("- export filter: " + itos(p_preset->get_export_filter()));
@@ -2737,14 +2729,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_UNCONFIGURED;
}
- if (use_custom_build) {
- print_verbose("Starting custom build...");
+ if (use_gradle_build) {
+ print_verbose("Starting gradle build...");
//test that installed build version is alright
{
print_verbose("Checking build version...");
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."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a gradle 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();
@@ -2814,11 +2806,11 @@ 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 = p_preset->get("custom_build/min_sdk");
+ String min_sdk_version = p_preset->get("gradle_build/min_sdk");
if (!min_sdk_version.is_valid_int()) {
- min_sdk_version = itos(DEFAULT_MIN_SDK_VERSION);
+ min_sdk_version = itos(VULKAN_MIN_SDK_VERSION);
}
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2944,7 +2936,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_CANT_CREATE;
}
- print_verbose("Successfully completed Android custom build.");
+ print_verbose("Successfully completed Android gradle build.");
return OK;
}
// This is the start of the Legacy build system
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index bff769fcba..337a0228d0 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -74,7 +74,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
Vector<PluginConfigAndroid> plugins;
String last_plugin_names;
- uint64_t last_custom_build_time = 0;
+ uint64_t last_gradle_build_time = 0;
SafeFlag plugins_changed;
Mutex plugins_lock;
Vector<Device> devices;
@@ -172,6 +172,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
+ static bool _uses_vulkan();
+
public:
typedef Error (*EditorExportSaveFunction)(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);
@@ -213,14 +215,14 @@ public:
inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins);
- bool first_build = last_custom_build_time == 0;
+ bool first_build = last_gradle_build_time == 0;
bool have_plugins_changed = false;
if (!first_build) {
have_plugins_changed = plugin_names != last_plugin_names;
if (!have_plugins_changed) {
for (int i = 0; i < enabled_plugins.size(); i++) {
- if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
+ if (enabled_plugins.get(i).last_updated > last_gradle_build_time) {
have_plugins_changed = true;
break;
}
@@ -228,7 +230,7 @@ public:
}
}
- last_custom_build_time = OS::get_singleton()->get_unix_time();
+ last_gradle_build_time = OS::get_singleton()->get_unix_time();
last_plugin_names = plugin_names;
return have_plugins_changed || first_build;
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index e450f3edb3..b889d58199 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -166,7 +166,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// This method will only be called as an input to export_project_files.
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
-// This method will be called ONLY when custom build is enabled.
+// This method will be called ONLY when gradle 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 = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
@@ -254,13 +254,11 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_screen_sizes;
}
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
+String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan) {
String manifest_xr_features;
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 == XR_HAND_TRACKING_OPTIONAL) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n";
@@ -275,27 +273,46 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n";
}
}
+
+ if (p_uses_vulkan) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"true\" android:version=\"1\" />\n";
+ }
return manifest_xr_features;
}
-String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
- int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
- bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
+String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr) {
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
+ "tools:node=\"mergeOnlyAttributes\" "
"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";
+
+ if (p_uses_xr) {
+ manifest_activity_text += " <intent-filter>\n"
+ " <action android:name=\"android.intent.action.MAIN\" />\n"
+ " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+ "\n"
+ " <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android\n"
+ " platforms. -->\n"
+ " <category android:name=\"com.oculus.intent.category.VR\" />\n"
+ "\n"
+ " <!-- OpenXR category tag to indicate the activity starts in an immersive OpenXR mode. \n"
+ " See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#android-runtime-category. -->\n"
+ " <category android:name=\"org.khronos.openxr.intent.category.IMMERSIVE_HMD\" />\n"
+ " </intent-filter>\n";
} else {
- manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n";
+ manifest_activity_text += " <intent-filter>\n"
+ " <action android:name=\"android.intent.action.MAIN\" />\n"
+ " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+ " </intent-filter>\n";
}
+
manifest_activity_text += " </activity>\n";
return manifest_activity_text;
}
@@ -316,9 +333,7 @@ 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:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\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",
+ " tools:ignore=\"GoogleAppIndexingWarning\">\n\n",
bool_to_string(p_preset->get("user_data_backup/allow")),
_get_app_category_label(app_category_index),
bool_to_string(is_game),
@@ -335,10 +350,8 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
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 += _get_activity_tag(p_preset, uses_xr);
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 0fa857cb75..8a885a0d12 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -104,7 +104,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
// This method will only be called as an input to export_project_files.
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
-// This method will be called ONLY when custom build is enabled.
+// This method will be called ONLY when gradle 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);
// Creates strings.xml files inside the gradle project for different locales.
@@ -116,9 +116,9 @@ String _get_gles_tag();
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset);
+String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan);
-String _get_activity_tag(const Ref<EditorExportPreset> &p_preset);
+String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr);
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission);
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 5df05580a5..1249f2219f 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -169,6 +169,10 @@ bool FileAccessAndroid::file_exists(const String &p_path) {
return true;
}
+void FileAccessAndroid::close() {
+ _close();
+}
+
FileAccessAndroid::~FileAccessAndroid() {
_close();
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 1d25a28d90..b8f45628e5 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -78,6 +78,8 @@ public:
virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual void close() override;
+
~FileAccessAndroid();
};
diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp
index 7174d57344..ea8459d1ed 100644
--- a/platform/android/file_access_filesystem_jandroid.cpp
+++ b/platform/android/file_access_filesystem_jandroid.cpp
@@ -333,6 +333,12 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
_file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J");
}
+void FileAccessFilesystemJAndroid::close() {
+ if (is_open()) {
+ _close();
+ }
+}
+
FileAccessFilesystemJAndroid::FileAccessFilesystemJAndroid() {
id = 0;
}
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 7829ab7cf9..5e74d9de24 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -93,6 +93,8 @@ public:
virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual void close() override;
+
FileAccessFilesystemJAndroid();
~FileAccessFilesystemJAndroid();
};
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 8c8608cbbb..ce4a2ecfe4 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -31,29 +31,6 @@
android:name="org.godotengine.editor.version"
android:value="${godotEditorVersion}" />
- <!-- 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"
@@ -66,16 +43,9 @@
android:resizeableActivity="false"
tools:ignore="UnusedAttribute" >
- <!-- Focus awareness metadata is removed at export time if the xr mode is not VR. -->
- <meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
-
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
-
- <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android
- platforms. -->
- <category android:name="com.oculus.intent.category.VR" />
</intent-filter>
</activity>
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 63b10e62b1..01b148aeef 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -50,7 +50,7 @@ dependencies {
} else if (rootProject.findProject(":godot:lib")) {
implementation project(":godot:lib")
} else {
- // Custom build mode. In this scenario this project is the only one around and the Godot
+ // Godot gradle 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'])
@@ -153,7 +153,7 @@ android {
debug {
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.debug
@@ -165,7 +165,7 @@ android {
dev {
initWith debug
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.debug
@@ -176,7 +176,7 @@ android {
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.release
diff --git a/platform/android/java/app/gradle.properties b/platform/android/java/app/gradle.properties
index 0ad8e611ca..d9f79b6818 100644
--- a/platform/android/java/app/gradle.properties
+++ b/platform/android/java/app/gradle.properties
@@ -1,5 +1,5 @@
-# Godot custom build Gradle settings.
-# These properties apply when running custom build from the Godot editor.
+# Godot gradle build settings.
+# These properties apply when running a gradle build from the Godot editor.
# NOTE: This should be kept in sync with 'godot/platform/android/java/gradle.properties' except
# where otherwise specified.
diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle
index ba53aefe7f..b4524a3f60 100644
--- a/platform/android/java/app/settings.gradle
+++ b/platform/android/java/app/settings.gradle
@@ -1,4 +1,4 @@
-// This is the root directory of the Godot custom build.
+// This is the root directory of the Godot Android gradle build.
pluginManagement {
apply from: 'config.gradle'
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 a43e289b6b..1d2cc05715 100644
--- a/platform/android/java/app/src/com/godot/game/GodotApp.java
+++ b/platform/android/java/app/src/com/godot/game/GodotApp.java
@@ -35,7 +35,7 @@ import org.godotengine.godot.FullScreenGodotApp;
import android.os.Bundle;
/**
- * Template activity for Godot Android custom builds.
+ * Template activity for Godot Android builds.
* Feel free to extend and modify this class for your custom logic.
*/
public class GodotApp extends FullScreenGodotApp {
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 5a91e5ce32..cffe0a33d9 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -152,14 +152,14 @@ task copyReleaseAARToBin(type: Copy) {
}
/**
- * Generate Godot custom build template by zipping the source files from the app directory, as well
+ * Generate Godot gradle build template by zipping the source files from the app directory, as well
* 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.
+ * The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
*/
-task zipCustomBuild(type: Zip) {
+task zipGradleBuild(type: Zip) {
onlyIf { generateGodotTemplates.state.executed || generateDevTemplate.state.executed }
doFirst {
- logger.lifecycle("Generating Godot custom build template")
+ logger.lifecycle("Generating Godot gradle build template")
}
from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))
include '**/*'
@@ -195,7 +195,7 @@ def templateBuildTasks() {
&& targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0) {
String capitalizedTarget = target.capitalize()
- // Copy the generated aar library files to the custom build directory.
+ // Copy the generated aar library files to the build directory.
tasks += "copy" + capitalizedTarget + "AARToAppModule"
// Copy the generated aar library files to the bin directory.
tasks += "copy" + capitalizedTarget + "AARToBin"
@@ -260,7 +260,7 @@ task generateGodotTemplates {
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = templateBuildTasks()
- finalizedBy 'zipCustomBuild'
+ finalizedBy 'zipGradleBuild'
}
/**
@@ -273,7 +273,7 @@ task generateDevTemplate {
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = templateBuildTasks()
- finalizedBy 'zipCustomBuild'
+ finalizedBy 'zipGradleBuild'
}
task clean(type: Delete) {
diff --git a/platform/android/java/editor/src/dev/res/values/strings.xml b/platform/android/java/editor/src/dev/res/values/strings.xml
index 45fae3fd39..215f2c7d0a 100644
--- a/platform/android/java/editor/src/dev/res/values/strings.xml
+++ b/platform/android/java/editor/src/dev/res/values/strings.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="godot_editor_name_string">Godot Editor 4.x (dev)</string>
+ <string name="godot_editor_name_string">Godot Editor 4 (dev)</string>
</resources>
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index f848089aa8..71385315ae 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -31,12 +31,11 @@
package org.godotengine.editor
import android.Manifest
+import android.app.ActivityManager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.os.Debug
-import android.os.Environment
+import android.os.*
import android.util.Log
import android.widget.Toast
import androidx.window.layout.WindowMetricsCalculator
@@ -64,11 +63,18 @@ open class GodotEditor : FullScreenGodotApp() {
private const val COMMAND_LINE_PARAMS = "command_line_params"
+ private const val EDITOR_ID = 777
private const val EDITOR_ARG = "--editor"
private const val EDITOR_ARG_SHORT = "-e"
+ private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor"
+ private const val GAME_ID = 667
+ private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame"
+
+ private const val PROJECT_MANAGER_ID = 555
private const val PROJECT_MANAGER_ARG = "--project-manager"
private const val PROJECT_MANAGER_ARG_SHORT = "-p"
+ private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager"
}
private val commandLineParams = ArrayList<String>()
@@ -102,9 +108,10 @@ open class GodotEditor : FullScreenGodotApp() {
override fun getCommandLine() = commandLineParams
- override fun onNewGodotInstanceRequested(args: Array<String>) {
+ override fun onNewGodotInstanceRequested(args: Array<String>): Int {
// Parse the arguments to figure out which activity to start.
var targetClass: Class<*> = GodotGame::class.java
+ var instanceId = GAME_ID
// Whether we should launch the new godot instance in an adjacent window
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
@@ -115,12 +122,14 @@ open class GodotEditor : FullScreenGodotApp() {
if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) {
targetClass = GodotEditor::class.java
launchAdjacent = false
+ instanceId = EDITOR_ID
break
}
if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) {
targetClass = GodotProjectManager::class.java
launchAdjacent = false
+ instanceId = PROJECT_MANAGER_ID
break
}
}
@@ -139,6 +148,37 @@ open class GodotEditor : FullScreenGodotApp() {
Log.d(TAG, "Starting $targetClass")
startActivity(newInstance)
}
+ return instanceId
+ }
+
+ override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
+ val processNameSuffix = when (godotInstanceId) {
+ GAME_ID -> {
+ GAME_PROCESS_NAME_SUFFIX
+ }
+ EDITOR_ID -> {
+ EDITOR_PROCESS_NAME_SUFFIX
+ }
+ PROJECT_MANAGER_ID -> {
+ PROJECT_MANAGER_PROCESS_NAME_SUFFIX
+ }
+ else -> ""
+ }
+ if (processNameSuffix.isBlank()) {
+ return false
+ }
+
+ val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ val runningProcesses = activityManager.runningAppProcesses
+ for (runningProcess in runningProcesses) {
+ if (runningProcess.processName.endsWith(processNameSuffix)) {
+ Log.v(TAG, "Killing Godot process ${runningProcess.processName}")
+ Process.killProcess(runningProcess.pid)
+ return true
+ }
+ }
+
+ return false
}
// Get the screen's density scale
diff --git a/platform/android/java/editor/src/main/res/values/dimens.xml b/platform/android/java/editor/src/main/res/values/dimens.xml
index 03fb6184d2..98bfe40179 100644
--- a/platform/android/java/editor/src/main/res/values/dimens.xml
+++ b/platform/android/java/editor/src/main/res/values/dimens.xml
@@ -1,5 +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>
+ <dimen name="editor_default_window_width">1024dp</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
index 837a5d62e1..216d02d9c7 100644
--- a/platform/android/java/editor/src/main/res/values/strings.xml
+++ b/platform/android/java/editor/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="godot_editor_name_string">Godot Editor 4.x</string>
+ <string name="godot_editor_name_string">Godot Editor 4</string>
<string name="denied_storage_permission_error_msg">Missing storage access permission!</string>
</resources>
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index 5cd94e85d9..39a0dcda16 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -24,5 +24,5 @@ org.gradle.jvmargs=-Xmx4536m
org.gradle.warning.mode=all
# Disable resource optimizations for template release build.
-# NOTE: This is turned on for custom build in order to improve the release build.
+# NOTE: This is turned on for Godot Editor's gradle builds in order to improve the release build.
android.enableResourceOptimizations=false
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index 1f77e2fc34..f03a1dd47a 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -20,6 +20,16 @@
android:exported="false"
/>
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${applicationId}.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/godot_provider_paths" />
+ </provider>
+
</application>
</manifest>
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index f5a4ab1071..03752e092e 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -14,6 +14,7 @@
<string name="text_button_cancel_verify">Cancel Verification</string>
<string name="text_error_title">Error!</string>
<string name="error_engine_setup_message">Unable to setup the Godot Engine! Aborting…</string>
+ <string name="error_missing_vulkan_requirements_message">Warning - this device does not meet the requirements for Vulkan support</string>
<!-- APK Expansion Strings -->
@@ -48,7 +49,7 @@
<string name="state_failed_unlicensed">Download failed because you may not have purchased this app</string>
<string name="state_failed_fetching_url">Download failed because the resources could not be found</string>
<string name="state_failed_sdcard_full">Download failed because the external storage is full</string>
- <string name="state_failed_cancelled">Download cancelled</string>
+ <string name="state_failed_cancelled">Download canceled</string>
<string name="state_failed">Download failed</string>
<string name="kilobytes_per_second">%1$s KB/s</string>
diff --git a/platform/android/java/lib/res/xml/godot_provider_paths.xml b/platform/android/java/lib/res/xml/godot_provider_paths.xml
new file mode 100644
index 0000000000..1255f576bf
--- /dev/null
+++ b/platform/android/java/lib/res/xml/godot_provider_paths.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <external-path
+ name="public"
+ path="." />
+
+ <external-files-path
+ name="app"
+ path="." />
+</paths>
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 863e67f1e9..677c9d8f13 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -74,28 +74,36 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
public void onDestroy() {
Log.v(TAG, "Destroying Godot app...");
super.onDestroy();
- onGodotForceQuit(godotFragment);
+ terminateGodotInstance(godotFragment);
}
@Override
public final void onGodotForceQuit(Godot instance) {
+ runOnUiThread(() -> {
+ terminateGodotInstance(instance);
+ });
+ }
+
+ private void terminateGodotInstance(Godot instance) {
if (instance == godotFragment) {
Log.v(TAG, "Force quitting Godot instance");
- ProcessPhoenix.forceQuit(this);
+ ProcessPhoenix.forceQuit(FullScreenGodotApp.this);
}
}
@Override
public final void onGodotRestartRequested(Godot instance) {
- if (instance == godotFragment) {
- // 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).
- Log.v(TAG, "Restarting Godot instance...");
- ProcessPhoenix.triggerRebirth(this);
- }
+ runOnUiThread(() -> {
+ if (instance == godotFragment) {
+ // 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 static data).
+ Log.v(TAG, "Restarting Godot instance...");
+ ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this);
+ }
+ });
}
@Override
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 905db13c85..a03da7292b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -83,6 +83,7 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
@@ -258,13 +259,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
*/
@Keep
private boolean onVideoInit() {
- final Activity activity = getActivity();
+ final Activity activity = requireActivity();
containerLayout = new FrameLayout(activity);
containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// GodotEditText layout
GodotEditText editText = new GodotEditText(activity);
- editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+ editText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
(int)getResources().getDimension(R.dimen.text_edit_height)));
// ...add to FrameLayout
containerLayout.addView(editText);
@@ -275,11 +276,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
return false;
}
- final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
- if (renderer.equals("gl_compatibility")) {
- mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
- } else {
+ if (usesVulkan()) {
+ if (!meetsVulkanRequirements(activity.getPackageManager())) {
+ Log.w(TAG, "Missing requirements for vulkan support!");
+ }
mRenderView = new GodotVulkanRenderView(activity, this);
+ } else {
+ // Fallback to openGl
+ mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
}
View view = mRenderView.getView();
@@ -317,6 +321,26 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
return true;
}
+ /**
+ * Returns true if `Vulkan` is used for rendering.
+ */
+ private boolean usesVulkan() {
+ final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
+ final String renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver");
+ return ("forward_plus".equals(renderer) || "mobile".equals(renderer)) && "vulkan".equals(renderingDevice);
+ }
+
+ /**
+ * Returns true if the device meets the base requirements for Vulkan support, false otherwise.
+ */
+ private boolean meetsVulkanRequirements(@Nullable PackageManager packageManager) {
+ if (packageManager == null) {
+ return false;
+ }
+
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1);
+ }
+
public void setKeepScreenOn(final boolean p_enabled) {
runOnUiThread(() -> {
if (p_enabled) {
@@ -348,11 +372,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void restart() {
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
- });
+ if (godotHost != null) {
+ godotHost.onGodotRestartRequested(this);
+ }
}
public void alert(final String message, final String title) {
@@ -889,11 +911,20 @@ 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.
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onGodotForceQuit(this);
- }
- });
+ forceQuit(0);
+ }
+
+ @Keep
+ private boolean forceQuit(int instanceId) {
+ if (godotHost == null) {
+ return false;
+ }
+ if (instanceId == 0) {
+ godotHost.onGodotForceQuit(this);
+ return true;
+ } else {
+ return godotHost.onGodotForceQuit(instanceId);
+ }
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
@@ -1052,11 +1083,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
- private void createNewGodotInstance(String[] args) {
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onNewGodotInstanceRequested(args);
- }
- });
+ private int createNewGodotInstance(String[] args) {
+ if (godotHost != null) {
+ return godotHost.onNewGodotInstanceRequested(args);
+ }
+ return 0;
}
}
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 256d04e3a5..7700b9b628 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -55,21 +55,35 @@ public interface GodotHost {
default void onGodotMainLoopStarted() {}
/**
- * Invoked on the UI thread as the last step of the Godot instance clean up phase.
+ * Invoked on the render thread to terminate the given Godot instance.
*/
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
+ * Invoked on the render thread to terminate the Godot instance with the given id.
+ * @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested}
+ *
+ * @return true if successful, false otherwise.
+ */
+ default boolean onGodotForceQuit(int godotInstanceId) {
+ return false;
+ }
+
+ /**
+ * Invoked on the render 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
+ * Invoked on the render 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.
+ *
+ * @return the id of the new instance. See {@code onGodotForceQuit}
*/
- default void onNewGodotInstanceRequested(String[] args) {}
+ default int onNewGodotInstanceRequested(String[] args) {
+ return 0;
+ }
}
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 aca0c4381b..edcd9c4d1f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -49,6 +49,9 @@ import android.view.Display;
import android.view.DisplayCutout;
import android.view.WindowInsets;
+import androidx.core.content.FileProvider;
+
+import java.io.File;
import java.util.List;
import java.util.Locale;
@@ -84,29 +87,42 @@ public class GodotIO {
// MISCELLANEOUS OS IO
/////////////////////////
- public int openURI(String p_uri) {
+ public int openURI(String uriString) {
try {
- String path = p_uri;
- String type = "";
- if (path.startsWith("/")) {
- //absolute path to filesystem, prepend file://
- path = "file://" + path;
- if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) {
- type = "image/*";
+ Uri dataUri;
+ String dataType = "";
+ boolean grantReadUriPermission = false;
+
+ if (uriString.startsWith("/") || uriString.startsWith("file://")) {
+ String filePath = uriString;
+ // File uris needs to be provided via the FileProvider
+ grantReadUriPermission = true;
+ if (filePath.startsWith("file://")) {
+ filePath = filePath.replace("file://", "");
}
+
+ File targetFile = new File(filePath);
+ dataUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", targetFile);
+ dataType = activity.getContentResolver().getType(dataUri);
+ } else {
+ dataUri = Uri.parse(uriString);
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
- if (!type.equals("")) {
- intent.setDataAndType(Uri.parse(path), type);
+ if (TextUtils.isEmpty(dataType)) {
+ intent.setData(dataUri);
} else {
- intent.setData(Uri.parse(path));
+ intent.setDataAndType(dataUri, dataType);
+ }
+ if (grantReadUriPermission) {
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
activity.startActivity(intent);
return 0;
} catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Unable to open uri " + uriString, e);
return 1;
}
}
@@ -150,7 +166,6 @@ public class GodotIO {
} else {
selectedScaledDensity = 0.75f;
}
- Log.d(TAG, "Selected scaled density: " + selectedScaledDensity);
return selectedScaledDensity;
}
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 31b512d2dd..75a01dc787 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -148,7 +148,7 @@ public class GodotLib {
/**
* Forward regular key events.
*/
- public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed);
+ public static native void key(int p_physical_keycode, int p_unicode, int p_key_label, boolean p_pressed);
/**
* Forward game device's key events.
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 f2bd3e28e6..cedbbfb7c3 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
@@ -129,12 +129,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
} else {
// getKeyCode(): The physical key that was pressed.
- // Godot's keycodes match the ASCII codes, so for single byte unicode characters,
- // we can use the unmodified unicode character to determine Godot's keycode.
- final int keycode = event.getUnicodeChar(0);
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
- GodotLib.key(keycode, physical_keycode, unicode, false);
+ final int key_label = event.getDisplayLabel();
+ GodotLib.key(physical_keycode, unicode, key_label, false);
};
return true;
@@ -166,10 +164,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
GodotLib.joybutton(godotJoyId, button, true);
}
} else {
- final int keycode = event.getUnicodeChar(0);
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
- GodotLib.key(keycode, physical_keycode, unicode, true);
+ final int key_label = event.getDisplayLabel();
+ GodotLib.key(physical_keycode, unicode, key_label, true);
}
return true;
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 fdfe20f32b..7b628e25ed 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
@@ -93,8 +93,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
for (int i = 0; i < count; ++i) {
- GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, true);
- GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, false);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, false);
if (mHasSelection) {
mHasSelection = false;
@@ -110,13 +110,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
newChars[i - start] = pCharSequence.charAt(i);
}
for (int i = 0; i < count; ++i) {
- int key = newChars[i];
- if ((key == '\n') && !(mEdit.getKeyboardType() == GodotEditText.VirtualKeyboardType.KEYBOARD_TYPE_MULTILINE)) {
+ final int character = newChars[i];
+ if ((character == '\n') && !(mEdit.getKeyboardType() == GodotEditText.VirtualKeyboardType.KEYBOARD_TYPE_MULTILINE)) {
// Return keys are handled through action events
continue;
}
- GodotLib.key(key, 0, key, true);
- GodotLib.key(key, 0, key, false);
+ GodotLib.key(0, character, 0, true);
+ GodotLib.key(0, character, 0, false);
}
}
@@ -126,17 +126,17 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
final String characters = pKeyEvent.getCharacters();
for (int i = 0; i < characters.length(); i++) {
- final int ch = characters.codePointAt(i);
- GodotLib.key(ch, 0, ch, true);
- GodotLib.key(ch, 0, ch, false);
+ final int character = characters.codePointAt(i);
+ GodotLib.key(0, character, 0, true);
+ GodotLib.key(0, character, 0, false);
}
}
if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
mRenderView.queueOnRenderThread(() -> {
- GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, true);
- GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, false);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, false);
});
mRenderView.getView().requestFocus();
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt
index 9649b0aecc..b9b7ebac6e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt
@@ -64,7 +64,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler.
override fun hasDirId(dirId: Int) = dirs.indexOfKey(dirId) >= 0
override fun dirOpen(path: String): Int {
- val assetsPath = getAssetsPath(path) ?: return INVALID_DIR_ID
+ val assetsPath = getAssetsPath(path)
try {
val files = assetManager.list(assetsPath) ?: return INVALID_DIR_ID
// Empty directories don't get added to the 'assets' directory, so
@@ -99,7 +99,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler.
}
override fun fileExists(path: String): Boolean {
- val assetsPath = getAssetsPath(path) ?: return false
+ val assetsPath = getAssetsPath(path)
try {
val files = assetManager.list(assetsPath) ?: return false
// Empty directories don't get added to the 'assets' directory, so
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
index 2cc37b627a..3ee3478fcb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
@@ -120,7 +120,7 @@ public final class ProcessPhoenix extends Activity {
/**
* 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
+ * This can be used to avoid initialization 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
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index e6cdd7932a..e7abe580f1 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -382,11 +382,11 @@ 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_physical_keycode, jint p_unicode, jboolean p_pressed) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed) {
if (step.get() <= 0) {
return;
}
- input_handler->process_key_event(p_keycode, p_physical_keycode, p_unicode, p_pressed);
+ input_handler->process_key_event(p_physical_keycode, p_unicode, p_key_label, p_pressed);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
@@ -488,7 +488,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
String permission = jstring_to_string(p_permission, env);
if (permission == "android.permission.RECORD_AUDIO" && p_result) {
- AudioDriver::get_singleton()->capture_start();
+ AudioDriver::get_singleton()->input_start();
}
if (os_android->get_main_loop()) {
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 3f07a8cfe1..0020ddffd2 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -49,7 +49,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JN
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, 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);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 03548d11f6..9d9d087896 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -60,7 +60,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
// get some Godot method pointers...
_on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
- _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
+ _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
_get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
@@ -77,7 +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");
+ _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I");
_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
// get some Activity method pointers...
@@ -179,14 +179,15 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
}
}
-void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
+bool GodotJavaWrapper::force_quit(JNIEnv *p_env, int p_instance_id) {
if (_finish) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
- ERR_FAIL_NULL(p_env);
- p_env->CallVoidMethod(godot_instance, _finish);
+ ERR_FAIL_NULL_V(p_env, false);
+ return p_env->CallBooleanMethod(godot_instance, _finish, p_instance_id);
}
+ return false;
}
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
@@ -345,14 +346,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) {
}
}
-void GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+int GodotJavaWrapper::create_new_godot_instance(List<String> args) {
if (_create_new_godot_instance) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL(env);
+ ERR_FAIL_NULL_V(env, 0);
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);
+ return env->CallIntMethod(godot_instance, _create_new_godot_instance, jargs);
+ } else {
+ return 0;
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 5dad2a3eb9..1bd79584d8 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -85,7 +85,7 @@ public:
void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);
- void force_quit(JNIEnv *p_env = nullptr);
+ bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0);
void set_keep_screen_on(bool p_enabled);
void alert(const String &p_message, const String &p_title);
int get_gles_version_code();
@@ -103,7 +103,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);
+ int create_new_godot_instance(List<String> args);
};
#endif // JAVA_GODOT_WRAPPER_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 942bf0a904..725fea8d54 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -695,8 +695,8 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) {
}
OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion) {
- display_size.width = 800;
- display_size.height = 600;
+ display_size.width = DEFAULT_WINDOW_WIDTH;
+ display_size.height = DEFAULT_WINDOW_HEIGHT;
use_apk_expansion = p_use_apk_expansion;
@@ -739,9 +739,19 @@ Error OS_Android::create_process(const String &p_path, const List<String> &p_arg
}
Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
- godot_java->create_new_godot_instance(p_arguments);
+ int instance_id = godot_java->create_new_godot_instance(p_arguments);
+ if (r_child_id) {
+ *r_child_id = instance_id;
+ }
return OK;
}
+Error OS_Android::kill(const ProcessID &p_pid) {
+ if (godot_java->force_quit(nullptr, p_pid)) {
+ return OK;
+ }
+ return OS_Unix::kill(p_pid);
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 68b6fefe33..53910b1498 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -86,6 +86,8 @@ private:
public:
static const char *ANDROID_EXEC_PATH;
+ static const int DEFAULT_WINDOW_WIDTH = 800;
+ static const int DEFAULT_WINDOW_HEIGHT = 600;
virtual void initialize_core() override;
virtual void initialize() override;
@@ -156,6 +158,7 @@ public:
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 Error kill(const ProcessID &p_pid) 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);