diff options
Diffstat (limited to 'platform/android')
108 files changed, 1888 insertions, 744 deletions
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index fb442f4c54..a10afa1df8 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -31,128 +31,9 @@ #ifndef ANDROID_KEYS_UTILS_H #define ANDROID_KEYS_UTILS_H +#include <android/input.h> #include <core/os/keyboard.h> -/* - * Android Key codes. - */ -enum { - AKEYCODE_UNKNOWN = 0, - AKEYCODE_SOFT_LEFT = 1, - AKEYCODE_SOFT_RIGHT = 2, - AKEYCODE_HOME = 3, - AKEYCODE_BACK = 4, - AKEYCODE_CALL = 5, - AKEYCODE_ENDCALL = 6, - AKEYCODE_0 = 7, - AKEYCODE_1 = 8, - AKEYCODE_2 = 9, - AKEYCODE_3 = 10, - AKEYCODE_4 = 11, - AKEYCODE_5 = 12, - AKEYCODE_6 = 13, - AKEYCODE_7 = 14, - AKEYCODE_8 = 15, - AKEYCODE_9 = 16, - AKEYCODE_STAR = 17, - AKEYCODE_POUND = 18, - AKEYCODE_DPAD_UP = 19, - AKEYCODE_DPAD_DOWN = 20, - AKEYCODE_DPAD_LEFT = 21, - AKEYCODE_DPAD_RIGHT = 22, - AKEYCODE_DPAD_CENTER = 23, - AKEYCODE_VOLUME_UP = 24, - AKEYCODE_VOLUME_DOWN = 25, - AKEYCODE_POWER = 26, - AKEYCODE_CAMERA = 27, - AKEYCODE_CLEAR = 28, - AKEYCODE_A = 29, - AKEYCODE_B = 30, - AKEYCODE_C = 31, - AKEYCODE_D = 32, - AKEYCODE_E = 33, - AKEYCODE_F = 34, - AKEYCODE_G = 35, - AKEYCODE_H = 36, - AKEYCODE_I = 37, - AKEYCODE_J = 38, - AKEYCODE_K = 39, - AKEYCODE_L = 40, - AKEYCODE_M = 41, - AKEYCODE_N = 42, - AKEYCODE_O = 43, - AKEYCODE_P = 44, - AKEYCODE_Q = 45, - AKEYCODE_R = 46, - AKEYCODE_S = 47, - AKEYCODE_T = 48, - AKEYCODE_U = 49, - AKEYCODE_V = 50, - AKEYCODE_W = 51, - AKEYCODE_X = 52, - AKEYCODE_Y = 53, - AKEYCODE_Z = 54, - AKEYCODE_COMMA = 55, - AKEYCODE_PERIOD = 56, - AKEYCODE_ALT_LEFT = 57, - AKEYCODE_ALT_RIGHT = 58, - AKEYCODE_SHIFT_LEFT = 59, - AKEYCODE_SHIFT_RIGHT = 60, - AKEYCODE_TAB = 61, - AKEYCODE_SPACE = 62, - AKEYCODE_SYM = 63, - AKEYCODE_EXPLORER = 64, - AKEYCODE_ENVELOPE = 65, - AKEYCODE_ENTER = 66, - AKEYCODE_DEL = 67, - AKEYCODE_GRAVE = 68, - AKEYCODE_MINUS = 69, - AKEYCODE_EQUALS = 70, - AKEYCODE_LEFT_BRACKET = 71, - AKEYCODE_RIGHT_BRACKET = 72, - AKEYCODE_BACKSLASH = 73, - AKEYCODE_SEMICOLON = 74, - AKEYCODE_APOSTROPHE = 75, - AKEYCODE_SLASH = 76, - AKEYCODE_AT = 77, - AKEYCODE_NUM = 78, - AKEYCODE_HEADSETHOOK = 79, - AKEYCODE_FOCUS = 80, // *Camera* focus - AKEYCODE_PLUS = 81, - AKEYCODE_MENU = 82, - AKEYCODE_NOTIFICATION = 83, - AKEYCODE_SEARCH = 84, - AKEYCODE_MEDIA_PLAY_PAUSE = 85, - AKEYCODE_MEDIA_STOP = 86, - AKEYCODE_MEDIA_NEXT = 87, - AKEYCODE_MEDIA_PREVIOUS = 88, - AKEYCODE_MEDIA_REWIND = 89, - AKEYCODE_MEDIA_FAST_FORWARD = 90, - AKEYCODE_MUTE = 91, - AKEYCODE_PAGE_UP = 92, - AKEYCODE_PAGE_DOWN = 93, - AKEYCODE_PICTSYMBOLS = 94, - AKEYCODE_SWITCH_CHARSET = 95, - AKEYCODE_BUTTON_A = 96, - AKEYCODE_BUTTON_B = 97, - AKEYCODE_BUTTON_C = 98, - AKEYCODE_BUTTON_X = 99, - AKEYCODE_BUTTON_Y = 100, - AKEYCODE_BUTTON_Z = 101, - AKEYCODE_BUTTON_L1 = 102, - AKEYCODE_BUTTON_R1 = 103, - AKEYCODE_BUTTON_L2 = 104, - AKEYCODE_BUTTON_R2 = 105, - AKEYCODE_BUTTON_THUMBL = 106, - AKEYCODE_BUTTON_THUMBR = 107, - AKEYCODE_BUTTON_START = 108, - AKEYCODE_BUTTON_SELECT = 109, - AKEYCODE_BUTTON_MODE = 110, - - // NOTE: If you add a new keycode here you must also add it to several other files. - // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. -}; - struct _WinTranslatePair { unsigned int keysym; unsigned int keycode; @@ -246,6 +127,8 @@ static _WinTranslatePair _ak_to_keycode[] = { { KEY_BACKSLASH, AKEYCODE_BACKSLASH }, { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, + { KEY_CONTROL, AKEYCODE_CTRL_LEFT }, + { KEY_CONTROL, AKEYCODE_CTRL_RIGHT }, { KEY_UNKNOWN, 0 } }; /* diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp index 1f140f7119..8b82733d2d 100644 --- a/platform/android/api/api.cpp +++ b/platform/android/api/api.cpp @@ -30,7 +30,7 @@ #include "api.h" -#include "core/engine.h" +#include "core/config/engine.h" #include "java_class_wrapper.h" #include "jni_singleton.h" diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h index e34f2a9f69..64da049407 100644 --- a/platform/android/api/java_class_wrapper.h +++ b/platform/android/api/java_class_wrapper.h @@ -31,7 +31,7 @@ #ifndef JAVA_CLASS_WRAPPER_H #define JAVA_CLASS_WRAPPER_H -#include "core/reference.h" +#include "core/object/reference.h" #ifdef ANDROID_ENABLED #include <android/log.h> @@ -47,7 +47,6 @@ class JavaClass : public Reference { #ifdef ANDROID_ENABLED enum ArgumentType{ - ARG_TYPE_VOID, ARG_TYPE_BOOLEAN, ARG_TYPE_BYTE, @@ -180,7 +179,7 @@ class JavaClass : public Reference { #endif public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; JavaClass(); }; @@ -196,7 +195,7 @@ class JavaObject : public Reference { #endif public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call(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); diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index ed69f8d6e4..7f20c354a0 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -31,8 +31,8 @@ #ifndef JNI_SINGLETON_H #define JNI_SINGLETON_H -#include <core/engine.h> -#include <core/variant.h> +#include <core/config/engine.h> +#include <core/variant/variant.h> #ifdef ANDROID_ENABLED #include <platform/android/jni_utils.h> #endif @@ -52,7 +52,7 @@ class JNISingleton : public Object { #endif public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + virtual Variant call(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); diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 09c981b3fa..1363c5ac1e 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -30,8 +30,8 @@ #include "audio_driver_jandroid.h" +#include "core/config/project_settings.h" #include "core/os/os.h" -#include "core/project_settings.h" #include "thread_jandroid.h" AudioDriverAndroid *AudioDriverAndroid::s_ad = nullptr; diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h index 9858a40822..b30711705b 100644 --- a/platform/android/audio_driver_opensl.h +++ b/platform/android/audio_driver_opensl.h @@ -42,7 +42,6 @@ class AudioDriverOpenSL : public AudioDriver { Mutex mutex; enum { - BUFFER_COUNT = 2 }; diff --git a/platform/android/detect.py b/platform/android/detect.py index 6da1e5f3d6..0accacb679 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -115,7 +115,8 @@ def configure(env): if env["android_arch"] == "x86_64": if get_platform(env["ndk_platform"]) < 21: print( - "WARNING: android_arch=x86_64 is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21" + "WARNING: android_arch=x86_64 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" @@ -136,7 +137,8 @@ def configure(env): 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" + "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" @@ -164,7 +166,7 @@ def configure(env): elif env["target"] == "debug": env.Append(LINKFLAGS=["-O0"]) env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"]) - env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"]) + env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED"]) env.Append(CPPFLAGS=["-UNDEBUG"]) # Compiler configuration @@ -231,7 +233,10 @@ def configure(env): 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() + CCFLAGS=( + "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden" + " -fno-strict-aliasing".split() + ) ) env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"]) diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index ca312b427f..ba75a4b10c 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "dir_access_jandroid.h" -#include "core/print_string.h" +#include "core/string/print_string.h" #include "file_access_jandroid.h" #include "string_android.h" #include "thread_jandroid.h" diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 1436d832de..e82a12ece5 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -31,14 +31,13 @@ #include "display_server_android.h" #include "android_keys_utils.h" -#include "core/project_settings.h" +#include "core/config/project_settings.h" #include "java_godot_io_wrapper.h" #include "java_godot_wrapper.h" #include "os_android.h" -#if defined(OPENGL_ENABLED) -#include "drivers/gles2/rasterizer_gles2.h" -#endif +#include <android/input.h> + #if defined(VULKAN_ENABLED) #include "drivers/vulkan/rendering_device_vulkan.h" #include "platform/android/vulkan/vulkan_context_android.h" @@ -58,7 +57,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const { //case FEATURE_HIDPI: //case FEATURE_ICON: //case FEATURE_IME: - //case FEATURE_MOUSE: + case FEATURE_MOUSE: //case FEATURE_MOUSE_WARP: //case FEATURE_NATIVE_DIALOG: //case FEATURE_NATIVE_ICON: @@ -140,8 +139,11 @@ Size2i DisplayServerAndroid::screen_get_size(int p_screen) const { } Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const { - Size2i display_size = OS_Android::get_singleton()->get_display_size(); - return Rect2i(0, 0, display_size.width, display_size.height); + 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]); } int DisplayServerAndroid::screen_get_dpi(int p_screen) const { @@ -155,12 +157,12 @@ 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, int p_max_length, int p_cursor_start, int p_cursor_end) { +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); if (godot_io_java->has_vk()) { - godot_io_java->show_vk(p_existing_text, p_max_length, p_cursor_start, p_cursor_end); + godot_io_java->show_vk(p_existing_text, p_multiline, p_max_length, p_cursor_start, p_cursor_end); } else { ERR_PRINT("Virtual keyboard not available"); } @@ -343,7 +345,7 @@ void DisplayServerAndroid::alert(const String &p_alert, const String &p_title) { } void DisplayServerAndroid::process_events() { - // Nothing to do + Input::get_singleton()->flush_accumulated_events(); } Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { @@ -360,7 +362,11 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { } DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - return memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); + DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); + if (r_error != OK) { + ds->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver"); + } + return ds; } void DisplayServerAndroid::register_android_driver() { @@ -394,6 +400,8 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on"); + buttons_state = 0; + #if defined(OPENGL_ENABLED) if (rendering_driver == "opengl") { bool gl_initialization_error = false; @@ -444,6 +452,8 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis #endif Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); + + r_error = OK; } DisplayServerAndroid::~DisplayServerAndroid() { @@ -480,17 +490,40 @@ void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p } } +void DisplayServerAndroid::_set_key_modifier_state(Ref<InputEventWithModifiers> ev) { + ev->set_shift(shift_mem); + ev->set_alt(alt_mem); + ev->set_metakey(meta_mem); + ev->set_control(control_mem); +} + void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) { Ref<InputEventKey> ev; ev.instance(); int val = p_unicode_char; int keycode = android_get_keysym(p_keycode); int phy_keycode = android_get_keysym(p_scancode); + + if (keycode == KEY_SHIFT) { + shift_mem = p_pressed; + } + if (keycode == KEY_ALT) { + alt_mem = p_pressed; + } + if (keycode == KEY_CONTROL) { + control_mem = p_pressed; + } + if (keycode == KEY_META) { + meta_mem = p_pressed; + } + 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); } else if (val == 61448) { @@ -503,12 +536,12 @@ void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int OS_Android::get_singleton()->main_loop_request_go_back(); } - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); } -void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector<DisplayServerAndroid::TouchPos> &p_points) { - switch (p_what) { - case 0: { //gesture begin +void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vector<DisplayServerAndroid::TouchPos> &p_points) { + switch (p_event) { + case AMOTION_EVENT_ACTION_DOWN: { //gesture begin if (touch.size()) { //end all if exist for (int i = 0; i < touch.size(); i++) { @@ -517,7 +550,7 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(touch[i].id); ev->set_pressed(false); ev->set_position(touch[i].pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); } } @@ -534,11 +567,11 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(touch[i].id); ev->set_pressed(true); ev->set_position(touch[i].pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); } } break; - case 1: { //motion + case AMOTION_EVENT_ACTION_MOVE: { //motion ERR_FAIL_COND(touch.size() != p_points.size()); for (int i = 0; i < touch.size(); i++) { @@ -560,12 +593,13 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(touch[i].id); ev->set_position(p_points[idx].pos); ev->set_relative(p_points[idx].pos - touch[i].pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); touch.write[i].pos = p_points[idx].pos; } } break; - case 2: { //release + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_UP: { //release if (touch.size()) { //end all if exist for (int i = 0; i < touch.size(); i++) { @@ -574,12 +608,12 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(touch[i].id); ev->set_pressed(false); ev->set_position(touch[i].pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); } touch.clear(); } } break; - case 3: { // add touch + case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch for (int i = 0; i < p_points.size(); i++) { if (p_points[i].id == p_pointer) { TouchPos tp = p_points[i]; @@ -591,13 +625,13 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(tp.id); ev->set_pressed(true); ev->set_position(tp.pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); break; } } } break; - case 4: { // remove touch + case AMOTION_EVENT_ACTION_POINTER_UP: { // remove touch for (int i = 0; i < touch.size(); i++) { if (touch[i].id == p_pointer) { Ref<InputEventScreenTouch> ev; @@ -605,7 +639,7 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector ev->set_index(touch[i].id); ev->set_pressed(false); ev->set_position(touch[i].pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); touch.remove(i); break; @@ -618,36 +652,125 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) { // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER switch (p_type) { - case 7: // hover move - case 9: // hover enter - case 10: { // hover exit + case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move + case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter + case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit Ref<InputEventMouseMotion> ev; ev.instance(); + _set_key_modifier_state(ev); ev->set_position(p_pos); ev->set_global_position(p_pos); ev->set_relative(p_pos - hover_prev_pos); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); hover_prev_pos = p_pos; } break; } } -void DisplayServerAndroid::process_double_tap(Point2 p_pos) { +void DisplayServerAndroid::process_mouse_event(int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) { + int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask); + switch (event_action) { + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { + Ref<InputEventMouseButton> ev; + ev.instance(); + _set_key_modifier_state(ev); + ev->set_position(event_pos); + ev->set_global_position(event_pos); + ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS); + int changed_button_mask = buttons_state ^ event_buttons_mask; + + buttons_state = event_buttons_mask; + + ev->set_button_index(_button_index_from_mask(changed_button_mask)); + ev->set_button_mask(event_buttons_mask); + Input::get_singleton()->accumulate_input_event(ev); + } break; + + case AMOTION_EVENT_ACTION_MOVE: { + Ref<InputEventMouseMotion> ev; + ev.instance(); + _set_key_modifier_state(ev); + ev->set_position(event_pos); + ev->set_global_position(event_pos); + ev->set_relative(event_pos - hover_prev_pos); + ev->set_button_mask(event_buttons_mask); + Input::get_singleton()->accumulate_input_event(ev); + hover_prev_pos = event_pos; + } break; + case AMOTION_EVENT_ACTION_SCROLL: { + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_position(event_pos); + ev->set_global_position(event_pos); + ev->set_pressed(true); + buttons_state = event_buttons_mask; + if (event_vertical_factor > 0) { + _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, event_vertical_factor); + } else if (event_vertical_factor < 0) { + _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -event_vertical_factor); + } + + if (event_horizontal_factor > 0) { + _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, event_horizontal_factor); + } else if (event_horizontal_factor < 0) { + _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -event_horizontal_factor); + } + } break; + } +} + +void DisplayServerAndroid::_wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor) { + Ref<InputEventMouseButton> evd = ev->duplicate(); + _set_key_modifier_state(evd); + evd->set_button_index(wheel_button); + evd->set_button_mask(event_buttons_mask ^ (1 << (wheel_button - 1))); + evd->set_factor(factor); + Input::get_singleton()->accumulate_input_event(evd); + Ref<InputEventMouseButton> evdd = evd->duplicate(); + evdd->set_pressed(false); + evdd->set_button_mask(event_buttons_mask); + Input::get_singleton()->accumulate_input_event(evdd); +} + +void DisplayServerAndroid::process_double_tap(int event_android_button_mask, Point2 p_pos) { + int event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask); Ref<InputEventMouseButton> ev; ev.instance(); + _set_key_modifier_state(ev); ev->set_position(p_pos); ev->set_global_position(p_pos); - ev->set_pressed(false); + ev->set_pressed(event_button_mask != 0); + ev->set_button_index(_button_index_from_mask(event_button_mask)); + ev->set_button_mask(event_button_mask); ev->set_doubleclick(true); - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->accumulate_input_event(ev); +} + +int DisplayServerAndroid::_button_index_from_mask(int button_mask) { + switch (button_mask) { + case BUTTON_MASK_LEFT: + return BUTTON_LEFT; + case BUTTON_MASK_RIGHT: + return BUTTON_RIGHT; + case BUTTON_MASK_MIDDLE: + return BUTTON_MIDDLE; + case BUTTON_MASK_XBUTTON1: + return BUTTON_XBUTTON1; + case BUTTON_MASK_XBUTTON2: + return BUTTON_XBUTTON2; + default: + return 0; + } } void DisplayServerAndroid::process_scroll(Point2 p_pos) { Ref<InputEventPanGesture> ev; ev.instance(); + _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); + Input::get_singleton()->accumulate_input_event(ev); scroll_prev_pos = p_pos; } @@ -666,3 +789,32 @@ void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) { void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) { Input::get_singleton()->set_gyroscope(p_gyroscope); } + +Point2i DisplayServerAndroid::mouse_get_position() const { + return hover_prev_pos; +} + +int DisplayServerAndroid::mouse_get_button_state() const { + return buttons_state; +} + +int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_button_mask) { + int godot_button_mask = 0; + if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) { + godot_button_mask |= BUTTON_MASK_LEFT; + } + if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { + godot_button_mask |= BUTTON_MASK_RIGHT; + } + if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) { + godot_button_mask |= BUTTON_MASK_MIDDLE; + } + if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) { + godot_button_mask |= BUTTON_MASK_XBUTTON1; + } + if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { + godot_button_mask |= BUTTON_MASK_XBUTTON2; + } + + return godot_button_mask; +} diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index d64542df58..aa5a2c1185 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -63,6 +63,13 @@ public: private: String rendering_driver; + bool alt_mem = false; + bool shift_mem = false; + bool control_mem = false; + bool meta_mem = false; + + int buttons_state; + bool keep_screen_on; Vector<TouchPos> touch; @@ -84,6 +91,14 @@ private: static void _dispatch_input_events(const Ref<InputEvent> &p_event); + void _set_key_modifier_state(Ref<InputEventWithModifiers> ev); + + static int _button_index_from_mask(int button_mask); + + static int _android_button_mask_to_godot_button_mask(int android_button_mask); + + void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor); + public: static DisplayServerAndroid *get_singleton(); @@ -106,7 +121,7 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; - virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + 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); virtual void virtual_keyboard_hide(); virtual int virtual_keyboard_get_height() const; @@ -155,9 +170,10 @@ public: void process_gravity(const Vector3 &p_gravity); void process_magnetometer(const Vector3 &p_magnetometer); void process_gyroscope(const Vector3 &p_gyroscope); - void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); + void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points); void process_hover(int p_type, Point2 p_pos); - void process_double_tap(Point2 p_pos); + void process_mouse_event(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); @@ -168,6 +184,9 @@ public: void reset_window(); + virtual Point2i mouse_get_position() const; + virtual int mouse_get_button_state() const; + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 2f6f483edf..d24c96f87a 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -30,19 +30,21 @@ #include "export.h" +#include "core/config/project_settings.h" #include "core/io/image_loader.h" #include "core/io/marshalls.h" #include "core/io/zip_io.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/project_settings.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/export/gradle_export_util.h" #include "platform/android/logo.gen.h" #include "platform/android/plugin/godot_plugin_config.h" #include "platform/android/run_icon.gen.h" @@ -198,6 +200,9 @@ static const char *android_perms[] = { nullptr }; +static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable/splash.png"; +static const char *SPLASH_BG_COLOR_PATH = "res/drawable/splash_bg_color.png"; + struct LauncherIcon { const char *export_path; int dimensions; @@ -451,7 +456,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String name; bool first = true; for (int i = 0; i < basename.length(); i++) { - CharType c = basename[i]; + char32_t c = basename[i]; if (c >= '0' && c <= '9' && first) { continue; } @@ -482,7 +487,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { int segments = 0; bool first = true; for (int i = 0; i < pname.length(); i++) { - CharType c = pname[i]; + char32_t c = pname[i]; if (first && c == '.') { if (r_error) { *r_error = TTR("Package segments must be of non-zero length."); @@ -586,7 +591,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { zipfi.tmz_date.tm_hour = time.hour; zipfi.tmz_date.tm_mday = date.day; zipfi.tmz_date.tm_min = time.min; - zipfi.tmz_date.tm_mon = date.month; + zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed zipfi.tmz_date.tm_sec = time.sec; zipfi.tmz_date.tm_year = date.year; zipfi.dosDate = 0; @@ -721,7 +726,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { return OK; } - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + static Error 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; String dst_path = p_path.replace_first("res://", "assets/"); @@ -729,10 +734,67 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { return OK; } - static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + static Error ignore_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) { return OK; } + void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { + const char **aperms = android_perms; + while (*aperms) { + bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); + if (enabled) { + r_permissions.push_back("android.permission." + String(*aperms)); + } + aperms++; + } + PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); + for (int i = 0; i < user_perms.size(); i++) { + String user_perm = user_perms[i].strip_edges(); + if (!user_perm.empty()) { + r_permissions.push_back(user_perm); + } + } + if (p_give_internet) { + if (r_permissions.find("android.permission.INTERNET") == -1) { + r_permissions.push_back("android.permission.INTERNET"); + } + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1 /* XRMode.OVR */) { + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index > 0) { + if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { + r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); + } + } + } + } + + void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { + String manifest_text = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " xmlns:tools=\"http://schemas.android.com/tools\">\n"; + + manifest_text += _get_screen_sizes_tag(p_preset); + manifest_text += _get_gles_tag(); + + Vector<String> perms; + _get_permissions(p_preset, p_give_internet, perms); + for (int i = 0; i < perms.size(); i++) { + manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); + } + + manifest_text += _get_xr_features_tag(p_preset); + manifest_text += _get_instrumentation_tag(p_preset); + String plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); + manifest_text += _get_application_tag(p_preset, plugins_names); + manifest_text += "</manifest>\n"; + String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); + store_string_at_path(manifest_path, manifest_text); + } + void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { // Leaving the unused types commented because looking these constants up // again later would be annoying @@ -764,7 +826,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { int version_code = p_preset->get("version/code"); String package_name = p_preset->get("package/unique_name"); - int orientation = p_preset->get("screen/orientation"); + const int screen_orientation = _get_android_orientation_value(_get_screen_orientation()); bool screen_support_small = p_preset->get("screen/support_small"); bool screen_support_normal = p_preset->get("screen/support_normal"); @@ -777,30 +839,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); Vector<String> perms; - - const char **aperms = android_perms; - while (*aperms) { - bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); - if (enabled) { - perms.push_back("android.permission." + String(*aperms)); - } - aperms++; - } - - PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); - - for (int i = 0; i < user_perms.size(); i++) { - String user_perm = user_perms[i].strip_edges(); - if (!user_perm.empty()) { - perms.push_back(user_perm); - } - } - - if (p_give_internet) { - if (perms.find("android.permission.INTERNET") == -1) { - perms.push_back("android.permission.INTERNET"); - } - } + // Write permissions into the perms variable. + _get_permissions(p_preset, p_give_internet, perms); while (ofs < (uint32_t)p_manifest.size()) { uint32_t chunk = decode_uint32(&p_manifest[ofs]); @@ -836,7 +876,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (string_flags & UTF8_FLAG) { } else { uint32_t len = decode_uint16(&p_manifest[string_at]); - Vector<CharType> ucstring; + Vector<char32_t> ucstring; ucstring.resize(len + 1); for (uint32_t j = 0; j < len; j++) { uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); @@ -896,7 +936,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } if (tname == "activity" && attrname == "screenOrientation") { - encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]); + encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); } if (tname == "supports-screens") { @@ -971,10 +1011,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { feature_names.push_back("oculus.software.handtracking"); feature_required_list.push_back(hand_tracking_index == 2); feature_versions.push_back(-1); // no version attribute should be added. - - if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) { - perms.push_back("com.oculus.permission.HAND_TRACKING"); - } } } @@ -1301,7 +1337,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } else { String str; for (uint32_t i = 0; i < len; i++) { - CharType c = decode_uint16(&p_bytes[offset + i * 2]); + char32_t c = decode_uint16(&p_bytes[offset + i * 2]); if (c == 0) { break; } @@ -1310,12 +1346,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { return str; } } - void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) { + + void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) { const int UTF8_FLAG = 0x00000100; - uint32_t string_block_len = decode_uint32(&p_manifest[16]); - uint32_t string_count = decode_uint32(&p_manifest[20]); - uint32_t string_flags = decode_uint32(&p_manifest[28]); + uint32_t string_block_len = decode_uint32(&r_manifest[16]); + uint32_t string_count = decode_uint32(&r_manifest[20]); + uint32_t string_flags = decode_uint32(&r_manifest[28]); const uint32_t string_table_begins = 40; Vector<String> string_table; @@ -1323,10 +1360,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String package_name = p_preset->get("package/name"); for (uint32_t i = 0; i < string_count; i++) { - uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]); + uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); offset += string_table_begins + string_count * 4; - String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG); + String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG); if (str.begins_with("godot-project-name")) { if (str == "godot-project-name") { @@ -1334,7 +1371,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { str = get_project_name(package_name); } else { - String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_"); + 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); @@ -1352,7 +1389,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { ret.resize(string_table_begins + string_table.size() * 4); for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = p_manifest[i]; + ret.write[i] = r_manifest[i]; } int ofs = 0; @@ -1387,35 +1424,155 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { //append the rest... int rest_from = 12 + string_block_len; int rest_to = ret.size(); - int rest_len = (p_manifest.size() - rest_from); - ret.resize(ret.size() + (p_manifest.size() - rest_from)); + int rest_len = (r_manifest.size() - rest_from); + ret.resize(ret.size() + (r_manifest.size() - rest_from)); for (int i = 0; i < rest_len; i++) { - ret.write[rest_to + i] = p_manifest[rest_from + i]; + ret.write[rest_to + i] = r_manifest[rest_from + i]; } //finally update the size encode_uint32(ret.size(), &ret.write[4]); - p_manifest = ret; + r_manifest = ret; //printf("end\n"); } - void _process_launcher_icons(const String &p_processing_file_name, const Ref<Image> &p_source_image, const LauncherIcon p_icon, Vector<uint8_t> &p_data) { - if (p_processing_file_name == p_icon.export_path) { - Ref<Image> working_image = p_source_image; + void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) { + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert splash image to png."); + WARN_PRINT(err_str.utf8().get_data()); + } + } + + void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) { + Ref<Image> working_image = p_source_image; + + if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) { + working_image = p_source_image->duplicate(); + working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS); + } + + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png."; + WARN_PRINT(err_str.utf8().get_data()); + } + } + + void load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { + // TODO: Figure out how to handle remaining boot splash parameters (e.g: fullsize, filter) + String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - if (p_source_image->get_width() != p_icon.dimensions || p_source_image->get_height() != p_icon.dimensions) { - working_image = p_source_image->duplicate(); - working_image->resize(p_icon.dimensions, p_icon.dimensions, Image::Interpolation::INTERPOLATE_LANCZOS); + if (!project_splash_path.empty()) { + splash_image.instance(); + const Error err = ImageLoader::load_image(project_splash_path, splash_image); + if (err) { + splash_image.unref(); } + } - Vector<uint8_t> png_buffer; - Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); - if (err == OK) { - p_data.resize(png_buffer.size()); - memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); - } else { - String err_str = String("Failed to convert resized icon (") + p_processing_file_name + ") to png."; - WARN_PRINT(err_str.utf8().get_data()); + if (splash_image.is_null()) { + // Use the default + splash_image = Ref<Image>(memnew(Image(boot_splash_png))); + } + + // Setup the splash bg color + bool bg_color_valid; + Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); + if (!bg_color_valid) { + bg_color = boot_splash_bg_color; + } + + splash_bg_color_image.instance(); + splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->fill(bg_color); + } + + void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { + String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + + icon.instance(); + foreground.instance(); + background.instance(); + + // Regular icon: user selection -> project icon -> default. + String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); + if (path.empty() || ImageLoader::load_image(path, icon) != OK) { + ImageLoader::load_image(project_icon_path, icon); + } + + // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); + if (path.empty() || ImageLoader::load_image(path, foreground) != OK) { + foreground = icon; + } + + // Adaptive background: user selection -> default. + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); + if (!path.empty()) { + ImageLoader::load_image(path, background); + } + } + + void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) { + store_image(launcher_icon.export_path, data); + } + + void store_image(const String &export_path, const Vector<uint8_t> &data) { + String img_path = export_path.insert(0, "res://android/build/"); + store_file_at_path(img_path, data); + } + + void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, + const Ref<Image> &splash_image, + const Ref<Image> &splash_bg_color_image, + const Ref<Image> &main_image, + const Ref<Image> &foreground, + const Ref<Image> &background) { + // Store the splash image + if (splash_image.is_valid() && !splash_image->empty()) { + Vector<uint8_t> data; + _load_image_data(splash_image, data); + store_image(SPLASH_IMAGE_EXPORT_PATH, data); + } + + // Store the splash bg color image + if (splash_bg_color_image.is_valid() && !splash_bg_color_image->empty()) { + Vector<uint8_t> data; + _load_image_data(splash_bg_color_image, data); + store_image(SPLASH_BG_COLOR_PATH, data); + } + + // Prepare images to be resized for the icons. If some image ends up being uninitialized, + // the default image from the export template will be used. + + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->empty()) { + Vector<uint8_t> data; + _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data); + store_image(launcher_icons[i], data); + } + + if (foreground.is_valid() && !foreground->empty()) { + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground, + launcher_adaptive_icon_foregrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_foregrounds[i], data); + } + + if (background.is_valid() && !background->empty()) { + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background, + launcher_adaptive_icon_backgrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_backgrounds[i], data); } } } @@ -1433,10 +1590,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } public: - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total); + 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); public: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override { String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); if (driver == "GLES2") { r_features->push_back("etc"); @@ -1452,16 +1609,11 @@ public: } } - virtual void get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); - 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/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 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::BOOL, "xr_features/focus_awareness"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); + virtual void get_export_options(List<ExportOption> *r_options) override { 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_template/use_custom_build"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), 0)); Vector<PluginConfig> plugins_configs = get_plugins(); for (int i = 0; i < plugins_configs.size(); i++) { @@ -1470,39 +1622,52 @@ public: } plugins_changed = false; - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + 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)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); + 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::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::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/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 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::BOOL, "xr_features/focus_awareness"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/opengl_debug"), 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::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); - 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)); - } - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray())); const char **perms = android_perms; @@ -1512,19 +1677,19 @@ public: } } - virtual String get_name() const { + virtual String get_name() const override { return "Android"; } - virtual String get_os_name() const { + virtual String get_os_name() const override { return "Android"; } - virtual Ref<Texture2D> get_logo() const { + virtual Ref<Texture2D> get_logo() const override { return logo; } - virtual bool should_update_export_options() { + virtual bool should_update_export_options() override { bool export_options_changed = plugins_changed; if (export_options_changed) { // don't clear unless we're reporting true, to avoid race @@ -1533,7 +1698,7 @@ public: return export_options_changed; } - virtual bool poll_export() { + virtual bool poll_export() override { bool dc = devices_changed; if (dc) { // don't clear unless we're reporting true, to avoid race @@ -1542,22 +1707,22 @@ public: return dc; } - virtual int get_options_count() const { + virtual int get_options_count() const override { MutexLock lock(device_lock); return devices.size(); } - virtual String get_options_tooltip() const { + virtual String get_options_tooltip() const override { return TTR("Select device from the list"); } - virtual String get_option_label(int p_index) const { + virtual String get_option_label(int p_index) const override { ERR_FAIL_INDEX_V(p_index, devices.size(), ""); MutexLock lock(device_lock); return devices[p_index].name; } - virtual String get_option_tooltip(int p_index) const { + virtual String get_option_tooltip(int p_index) const override { ERR_FAIL_INDEX_V(p_index, devices.size(), ""); MutexLock lock(device_lock); String s = devices[p_index].description; @@ -1570,7 +1735,7 @@ public: return s; } - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override { ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); String can_export_error; @@ -1727,11 +1892,11 @@ public: #undef CLEANUP_AND_RETURN } - virtual Ref<Texture2D> get_run_icon() const { + virtual Ref<Texture2D> get_run_icon() const override { return run_icon; } - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { String err; bool valid = false; @@ -1809,9 +1974,12 @@ public: valid = false; } else { Error errn; + // Check for the platform-tools directory. DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); if (errn != OK) { - err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n"; + err += TTR("Invalid Android SDK path for custom build in Editor Settings."); + err += TTR("Missing 'platform-tools' directory!"); + err += "\n"; valid = false; } } @@ -1882,13 +2050,21 @@ public: } } + if (int(p_preset->get("custom_template/export_format")) == 1 && /*AAB*/ + !bool(p_preset->get("custom_template/use_custom_build"))) { + valid = false; + err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); + err += "\n"; + } + r_error = err; return valid; } - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { List<String> list; list.push_back("apk"); + list.push_back("aab"); return list; } @@ -1915,36 +2091,279 @@ public: return have_plugins_changed || first_build; } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { + String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + int version_code = p_preset->get("version/code"); + String package_name = p_preset->get("package/unique_name"); + String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; + String fullpath = p_path.get_base_dir().plus_file(apk_file_name); + return fullpath; + } + + Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + Error err = save_pack(p_preset, fullpath); + return err; + } + + void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { + String cmdline = p_preset->get("command_line/extra_args"); + 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); + i--; + } + } + + gen_export_flags(command_line_strings, p_flags); + + bool apk_expansion = p_preset->get("apk_expansion/enable"); + if (apk_expansion) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + String apk_expansion_public_key = p_preset->get("apk_expansion/public_key"); + + command_line_strings.push_back("--use_apk_expansion"); + command_line_strings.push_back("--apk_expansion_md5"); + command_line_strings.push_back(FileAccess::get_md5(fullpath)); + command_line_strings.push_back("--apk_expansion_key"); + command_line_strings.push_back(apk_expansion_public_key.strip_edges()); + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1) { + command_line_strings.push_back("--xr_mode_ovr"); + } 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"); + } + + bool debug_opengl = p_preset->get("graphics/opengl_debug"); + if (debug_opengl) { + command_line_strings.push_back("--debug_opengl"); + } + + if (command_line_strings.size()) { + r_command_line_flags.resize(4); + encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]); + for (int i = 0; i < command_line_strings.size(); i++) { + print_line(itos(i) + " param: " + command_line_strings[i]); + CharString command_line_argument = command_line_strings[i].utf8(); + int base = r_command_line_flags.size(); + int length = command_line_argument.length(); + if (length == 0) + continue; + r_command_line_flags.resize(base + 4 + length); + encode_uint32(length, &r_command_line_flags.write[base]); + copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); + } + } + } + + Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, String export_path, EditorProgress ep) { + int export_format = int(p_preset->get("custom_template/export_format")); + String export_label = export_format == 1 ? "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 jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner"); + if (!FileAccess::exists(jarsigner)) { + EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting " + export_label + " is unsigned."); + return OK; + } + + String keystore; + String password; + String user; + if (p_debug) { + keystore = p_preset->get("keystore/debug"); + password = p_preset->get("keystore/debug_password"); + user = p_preset->get("keystore/debug_user"); + + if (keystore.empty()) { + keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); + user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + } + + if (ep.step("Signing debug " + export_label + "...", 103)) { + return ERR_SKIP; + } + + } else { + keystore = release_keystore; + password = release_password; + user = release_username; + + if (ep.step("Signing release " + export_label + "...", 103)) { + return ERR_SKIP; + } + } + + if (!FileAccess::exists(keystore)) { + EditorNode::add_io_error("Could not find keystore, unable to export."); + return ERR_FILE_CANT_OPEN; + } + + List<String> args; + args.push_back("-digestalg"); + args.push_back("SHA-256"); + args.push_back("-sigalg"); + args.push_back("SHA256withRSA"); + String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url"); + if (tsa_url != "") { + args.push_back("-tsa"); + args.push_back(tsa_url); + } + args.push_back("-verbose"); + args.push_back("-keystore"); + args.push_back(keystore); + args.push_back("-storepass"); + args.push_back(password); + args.push_back(export_path); + args.push_back(user); + int retval; + OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval)); + return ERR_CANT_CREATE; + } + + if (ep.step("Verifying " + export_label + "...", 104)) { + return ERR_SKIP; + } + + args.clear(); + args.push_back("-verify"); + args.push_back("-keystore"); + args.push_back(keystore); + args.push_back(export_path); + args.push_back("-verbose"); + + OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' verification of " + export_label + " failed. Make sure to use a jarsigner from OpenJDK 8."); + return ERR_CANT_CREATE; + } + return OK; + } + + void _clear_assets_directory() { + DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da_res->dir_exists("res://android/build/assets")) { + DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); + da_assets->erase_contents_recursive(); + da_res->remove("res://android/build/assets"); + } + } + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); String src_apk; + Error err; EditorProgress ep("export", "Exporting for Android", 105, true); - if (bool(p_preset->get("custom_template/use_custom_build"))) { //custom build - //re-generate build.gradle and AndroidManifest.xml + bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build")); + int export_format = int(p_preset->get("custom_template/export_format")); + bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); + bool _signed = p_preset->get("package/signed"); + bool apk_expansion = p_preset->get("apk_expansion/enable"); + Vector<String> enabled_abis = get_enabled_abis(p_preset); + + Ref<Image> splash_image; + Ref<Image> splash_bg_color_image; + load_splash_refs(splash_image, splash_bg_color_image); + + Ref<Image> main_image; + Ref<Image> foreground; + Ref<Image> background; + + load_icon_refs(p_preset, main_image, foreground, background); + + Vector<uint8_t> command_line_flags; + // Write command line flags into the command_line_flags variable. + get_command_line_flags(p_preset, p_path, p_flags, command_line_flags); + + if (export_format == 1) { + if (!p_path.ends_with(".aab")) { + EditorNode::get_singleton()->show_warning(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.")); + return ERR_UNCONFIGURED; + } + } + if (export_format == 0 && !p_path.ends_with(".apk")) { + EditorNode::get_singleton()->show_warning( + TTR("Invalid filename! Android APK requires the *.apk extension.")); + return ERR_UNCONFIGURED; + } + if (export_format > 1 || export_format < 0) { + EditorNode::add_io_error("Unsupported export format!\n"); + return ERR_UNCONFIGURED; //TODO: is this the right error? + } - { //test that installed build version is alright + if (use_custom_build) { + //test that installed build version is alright + { 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.")); return ERR_UNCONFIGURED; } String version = f->get_line().strip_edges(); + f->close(); if (version != VERSION_FULL_CONFIG) { EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG)); return ERR_UNCONFIGURED; } } - //build project if custom build is enabled String sdk_path = EDITOR_GET("export/android/custom_build_sdk_path"); - ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/custom_build_sdk_path'."); - OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required + // TODO: should we use "package/name" or "application/config/name"? + 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("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, splash_image, splash_bg_color_image, main_image, foreground, background); + // Write an AndroidManifest.xml file into the Gradle project directory. + _write_tmp_manifest(p_preset, p_give_internet, p_debug); + + //stores all the project files inside the Gradle project directory. Also includes all ABIs + _clear_assets_directory(); + if (!apk_expansion) { + err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file); + if (err != OK) { + EditorNode::add_io_error("Could not export project files to gradle project\n"); + return err; + } + } else { + err = save_apk_expansion_file(p_preset, p_path); + if (err != OK) { + EditorNode::add_io_error("Could not write expansion package file!"); + return err; + } + } + store_file_at_path("res://android/build/assets/_cl_", command_line_flags); + OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required String build_command; + #ifdef WINDOWS_ENABLED build_command = "gradlew.bat"; #else @@ -1952,10 +2371,14 @@ public: #endif String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); - build_command = build_path.plus_file(build_command); 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 enabled_abi_string = String("|").join(enabled_abis); + String sign_flag = _signed ? "true" : "false"; + String zipalign_flag = "true"; Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset); String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins); @@ -1967,54 +2390,94 @@ public: if (clean_build_required) { cmdline.push_back("clean"); } - cmdline.push_back("build"); + + String build_type = p_debug ? "Debug" : "Release"; + if (export_format == 1) { + String bundle_build_command = vformat("bundle%s", build_type); + cmdline.push_back(bundle_build_command); + } else if (export_format == 0) { + String apk_build_command = vformat("assemble%s", build_type); + cmdline.push_back(apk_build_command); + } + 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_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. cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. + cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. + cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. + if (_signed && !p_debug) { + // Pass the release keystore info as well + 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 (!FileAccess::exists(release_keystore)) { + EditorNode::add_io_error("Could not find keystore, unable to export."); + return ERR_FILE_CANT_OPEN; + } + + cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file. + cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias. + cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specity the release keystore password. + } cmdline.push_back("-p"); // argument to specify the start directory. cmdline.push_back(build_path); // start directory. - /*{ used for debug - int ec; - String pipe; - OS::get_singleton()->execute(build_command, cmdline, true, nullptr, nullptr, &ec); - print_line("exit code: " + itos(ec)); - } - */ + 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.")); return ERR_CANT_CREATE; } - if (p_debug) { - src_apk = build_path.plus_file("build/outputs/apk/debug/android_debug.apk"); - } else { - src_apk = build_path.plus_file("build/outputs/apk/release/android_release.apk"); + + List<String> copy_args; + String copy_command; + if (export_format == 1) { + copy_command = vformat("copyAndRename%sAab", build_type); + } else if (export_format == 0) { + copy_command = vformat("copyAndRename%sApk", build_type); + } + + copy_args.push_back(copy_command); + + copy_args.push_back("-p"); // argument to specify the start directory. + copy_args.push_back(build_path); // start directory. + + String export_filename = p_path.get_file(); + String export_path = p_path.get_base_dir(); + if (export_path.is_rel_path()) { + export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); } + export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); + + copy_args.push_back("-Pexport_path=file:" + export_path); + copy_args.push_back("-Pexport_filename=" + export_filename); - if (!FileAccess::exists(src_apk)) { - EditorNode::get_singleton()->show_warning(TTR("No build apk generated at: ") + "\n" + src_apk); + 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.")); return ERR_CANT_CREATE; } - } else { + return OK; + } + // This is the start of the Legacy build system + if (p_debug) + src_apk = p_preset->get("custom_template/debug"); + else + src_apk = p_preset->get("custom_template/release"); + src_apk = src_apk.strip_edges(); + if (src_apk == "") { if (p_debug) { - src_apk = p_preset->get("custom_template/debug"); + src_apk = find_export_template("android_debug.apk"); } else { - src_apk = p_preset->get("custom_template/release"); + src_apk = find_export_template("android_release.apk"); } - - src_apk = src_apk.strip_edges(); if (src_apk == "") { - 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("Package not found: " + src_apk); - return ERR_FILE_NOT_FOUND; - } + EditorNode::add_io_error("Package not found: " + src_apk); + return ERR_FILE_NOT_FOUND; } } @@ -2051,57 +2514,13 @@ public: zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); - bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer"); - bool immersive = p_preset->get("screen/immersive_mode"); - bool debug_opengl = p_preset->get("screen/opengl_debug"); - - bool _signed = p_preset->get("package/signed"); - - bool apk_expansion = p_preset->get("apk_expansion/enable"); - String cmdline = p_preset->get("command_line/extra_args"); - int version_code = p_preset->get("version/code"); String version_name = p_preset->get("version/name"); String package_name = p_preset->get("package/unique_name"); String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - 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"); - - Vector<String> enabled_abis = get_enabled_abis(p_preset); - - String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); - - // Prepare images to be resized for the icons. If some image ends up being uninitialized, the default image from the export template will be used. - Ref<Image> launcher_icon_image; - Ref<Image> launcher_adaptive_icon_foreground_image; - Ref<Image> launcher_adaptive_icon_background_image; - - launcher_icon_image.instance(); - launcher_adaptive_icon_foreground_image.instance(); - launcher_adaptive_icon_background_image.instance(); - - // Regular icon: user selection -> project icon -> default. - String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); - if (path.empty() || ImageLoader::load_image(path, launcher_icon_image) != OK) { - ImageLoader::load_image(project_icon_path, launcher_icon_image); - } - - // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); - if (path.empty() || ImageLoader::load_image(path, launcher_adaptive_icon_foreground_image) != OK) { - launcher_adaptive_icon_foreground_image = launcher_icon_image; - } - - // Adaptive background: user selection -> default. - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); - if (!path.empty()) { - ImageLoader::load_image(path, launcher_adaptive_icon_background_image); - } - Vector<String> invalid_abis(enabled_abis); while (ret == UNZ_OK) { //get filename @@ -2122,24 +2541,38 @@ public: unzCloseCurrentFile(pkg); //write - if (file == "AndroidManifest.xml") { - _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); + _fix_manifest(p_preset, data, p_give_internet); } - if (file == "resources.arsc") { _fix_resources(p_preset, data); } + // Process the splash image + if (file == SPLASH_IMAGE_EXPORT_PATH && splash_image.is_valid() && !splash_image->empty()) { + _load_image_data(splash_image, data); + } + + // Process the splash bg color image + if (file == SPLASH_BG_COLOR_PATH && splash_bg_color_image.is_valid() && !splash_bg_color_image->empty()) { + _load_image_data(splash_bg_color_image, data); + } + for (int i = 0; i < icon_densities_count; ++i) { - if (launcher_icon_image.is_valid() && !launcher_icon_image->empty()) { - _process_launcher_icons(file, launcher_icon_image, launcher_icons[i], data); + if (main_image.is_valid() && !main_image->empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } } - if (launcher_adaptive_icon_foreground_image.is_valid() && !launcher_adaptive_icon_foreground_image->empty()) { - _process_launcher_icons(file, launcher_adaptive_icon_foreground_image, launcher_adaptive_icon_foregrounds[i], data); + if (foreground.is_valid() && !foreground->empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } } - if (launcher_adaptive_icon_background_image.is_valid() && !launcher_adaptive_icon_background_image->empty()) { - _process_launcher_icons(file, launcher_adaptive_icon_background_image, launcher_adaptive_icon_backgrounds[i], data); + if (background.is_valid() && !background->empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } } } @@ -2197,16 +2630,7 @@ public: if (ep.step("Adding files...", 1)) { CLEANUP_AND_RETURN(ERR_SKIP); } - Error err = OK; - Vector<String> cl = cmdline.strip_edges().split(" "); - for (int i = 0; i < cl.size(); i++) { - if (cl[i].strip_edges().length() == 0) { - cl.remove(i); - i--; - } - } - - gen_export_flags(cl, p_flags); + err = OK; if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { APKExportData ed; @@ -2214,90 +2638,39 @@ public: ed.apk = unaligned_apk; err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); } else { - //all files - if (apk_expansion) { - String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; - String fullpath = p_path.get_base_dir().plus_file(apkfname); - err = save_pack(p_preset, fullpath); - + err = save_apk_expansion_file(p_preset, p_path); if (err != OK) { - unzClose(pkg); - EditorNode::add_io_error("Could not write expansion package file: " + apkfname); - - CLEANUP_AND_RETURN(ERR_SKIP); + EditorNode::add_io_error("Could not write expansion package file!"); + return err; } - - cl.push_back("--use_apk_expansion"); - cl.push_back("--apk_expansion_md5"); - cl.push_back(FileAccess::get_md5(fullpath)); - cl.push_back("--apk_expansion_key"); - cl.push_back(apk_expansion_pkey.strip_edges()); - } else { APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; - err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); } } - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1 /* XRMode.OVR */) { - cl.push_back("--xr_mode_ovr"); - } else { - // XRMode.REGULAR is the default. - cl.push_back("--xr_mode_regular"); - } - - if (use_32_fb) { - cl.push_back("--use_depth_32"); - } - - if (immersive) { - cl.push_back("--use_immersive"); - } - - if (debug_opengl) { - cl.push_back("--debug_opengl"); - } - - if (cl.size()) { - //add comandline - Vector<uint8_t> clf; - clf.resize(4); - encode_uint32(cl.size(), &clf.write[0]); - for (int i = 0; i < cl.size(); i++) { - print_line(itos(i) + " param: " + cl[i]); - CharString txt = cl[i].utf8(); - int base = clf.size(); - int length = txt.length(); - if (!length) { - continue; - } - clf.resize(base + 4 + length); - encode_uint32(length, &clf.write[base]); - copymem(&clf.write[base + 4], txt.ptr(), length); - } - - zip_fileinfo zipfi = get_zip_fileinfo(); - - zipOpenNewFileInZip(unaligned_apk, - "assets/_cl_", - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - 0, // No compress (little size gain and potentially slower startup) - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(unaligned_apk, clf.ptr(), clf.size()); - zipCloseFileInZip(unaligned_apk); + if (err != OK) { + unzClose(pkg); + EditorNode::add_io_error("Could not export project files"); + CLEANUP_AND_RETURN(ERR_SKIP); } + zip_fileinfo zipfi = get_zip_fileinfo(); + zipOpenNewFileInZip(unaligned_apk, + "assets/_cl_", + &zipfi, + NULL, + 0, + NULL, + 0, + NULL, + 0, // No compress (little size gain and potentially slower startup) + Z_DEFAULT_COMPRESSION); + zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); + zipCloseFileInZip(unaligned_apk); zipClose(unaligned_apk, nullptr); unzClose(pkg); @@ -2306,84 +2679,9 @@ public: } if (_signed) { - String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner"); - if (!FileAccess::exists(jarsigner)) { - EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned."); - CLEANUP_AND_RETURN(OK); - } - - String keystore; - String password; - String user; - if (p_debug) { - keystore = p_preset->get("keystore/debug"); - password = p_preset->get("keystore/debug_password"); - user = p_preset->get("keystore/debug_user"); - - if (keystore.empty()) { - keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - } - - if (ep.step("Signing debug APK...", 103)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - } else { - keystore = release_keystore; - password = release_password; - user = release_username; - - if (ep.step("Signing release APK...", 103)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - } - - if (!FileAccess::exists(keystore)) { - EditorNode::add_io_error("Could not find keystore, unable to export."); - CLEANUP_AND_RETURN(ERR_FILE_CANT_OPEN); - } - - List<String> args; - args.push_back("-digestalg"); - args.push_back("SHA-256"); - args.push_back("-sigalg"); - args.push_back("SHA256withRSA"); - String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url"); - if (tsa_url != "") { - args.push_back("-tsa"); - args.push_back(tsa_url); - } - args.push_back("-verbose"); - args.push_back("-keystore"); - args.push_back(keystore); - args.push_back("-storepass"); - args.push_back(password); - args.push_back(tmp_unaligned_path); - args.push_back(user); - int retval; - OS::get_singleton()->execute(jarsigner, args, true, nullptr, nullptr, &retval); - if (retval) { - EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval)); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); - } - - if (ep.step("Verifying APK...", 104)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - args.clear(); - args.push_back("-verify"); - args.push_back("-keystore"); - args.push_back(keystore); - args.push_back(tmp_unaligned_path); - args.push_back("-verbose"); - - OS::get_singleton()->execute(jarsigner, args, true, nullptr, nullptr, &retval); - if (retval) { - EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8."); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); + err = sign_apk(p_preset, p_debug, tmp_unaligned_path, ep); + if (err != OK) { + CLEANUP_AND_RETURN(err); } } @@ -2442,12 +2740,10 @@ public: memset(extra + info.size_file_extra, 0, padding); - // write - zip_fileinfo zipfi = get_zip_fileinfo(); - + zip_fileinfo fileinfo = get_zip_fileinfo(); zipOpenNewFileInZip2(final_apk, file.utf8().get_data(), - &zipfi, + &fileinfo, extra, info.size_file_extra + padding, nullptr, @@ -2470,12 +2766,12 @@ public: CLEANUP_AND_RETURN(OK); } - virtual void get_platform_features(List<String> *r_features) { + virtual void get_platform_features(List<String> *r_features) override { r_features->push_back("mobile"); r_features->push_back("Android"); } - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { } EditorExportPlatformAndroid() { @@ -2511,7 +2807,7 @@ void register_android_exporter() { EDITOR_DEF("export/android/jarsigner", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext)); EDITOR_DEF("export/android/debug_keystore", ""); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore")); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks")); EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); EDITOR_DEF("export/android/debug_keystore_pass", "android"); EDITOR_DEF("export/android/force_system_user", false); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h new file mode 100644 index 0000000000..a9f38869e0 --- /dev/null +++ b/platform/android/export/gradle_export_util.h @@ -0,0 +1,306 @@ +/*************************************************************************/ +/* gradle_export_util.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 GODOT_GRADLE_EXPORT_UTIL_H +#define GODOT_GRADLE_EXPORT_UTIL_H + +#include "core/io/zip_io.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "editor/editor_export.h" + +const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="utf-8"?> +<!--WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">%s</string> +</resources> +)"; + +DisplayServer::ScreenOrientation _get_screen_orientation() { + String orientation_settings = ProjectSettings::get_singleton()->get("display/window/handheld/orientation"); + DisplayServer::ScreenOrientation screen_orientation; + if (orientation_settings == "portrait") + screen_orientation = DisplayServer::SCREEN_PORTRAIT; + else if (orientation_settings == "reverse_landscape") + screen_orientation = DisplayServer::SCREEN_REVERSE_LANDSCAPE; + else if (orientation_settings == "reverse_portrait") + screen_orientation = DisplayServer::SCREEN_REVERSE_PORTRAIT; + else if (orientation_settings == "sensor_landscape") + screen_orientation = DisplayServer::SCREEN_SENSOR_LANDSCAPE; + else if (orientation_settings == "sensor_portrait") + screen_orientation = DisplayServer::SCREEN_SENSOR_PORTRAIT; + else if (orientation_settings == "sensor") + screen_orientation = DisplayServer::SCREEN_SENSOR; + else + screen_orientation = DisplayServer::SCREEN_LANDSCAPE; + + return screen_orientation; +} + +int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return 1; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return 8; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return 9; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return 11; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return 12; + case DisplayServer::SCREEN_SENSOR: + return 13; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return 0; + } +} + +String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return "portrait"; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return "reverseLandscape"; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return "reversePortrait"; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return "userLandscape"; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return "userPortrait"; + case DisplayServer::SCREEN_SENSOR: + return "fullUser"; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return "landscape"; + } +} + +// 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 + "'."); + 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; +} + +// Implementation of EditorExportSaveSharedObject. +// This method will only be called as an input to export_project_files. +// This method lets the .so files for all ABIs to be copied +// into the gradle project from the .AAR file +Error ignore_so_file(void *p_userdata, const SharedObject &p_so) { + return OK; +} + +// Writes p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + 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 + "'."); + fa->store_buffer(p_data.ptr(), p_data.size()); + memdelete(fa); + return OK; +} + +// Writes string p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_string_at_path(const String &p_path, const String &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + 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 + "'."); + fa->store_string(p_data); + memdelete(fa); + return OK; +} + +// Implementation of EditorExportSaveFunction. +// 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. +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) { + String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); + Error err = store_file_at_path(dst_path, p_data); + return err; +} + +// 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) { + // Stores the string into the default values directory. + String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); + 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) { + return ERR_CANT_OPEN; + } + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + if (!file.begins_with("values-")) { + // NOTE: This assumes all directories that start with "values-" are for localization. + 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)); + store_string_at_path(locale_directory, processed_xml_string); + } else { + // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch + store_string_at_path(locale_directory, processed_default_xml_string); + } + } + da->list_dir_end(); + return OK; +} + +String bool_to_string(bool v) { + return v ? "true" : "false"; +} + +String _get_gles_tag() { + bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name") == "GLES3" && + !ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2"); + return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; +} + +String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\""; + String sizes[] = { "small", "normal", "large", "xlarge" }; + size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]); + for (size_t i = 0; i < num_sizes; i++) { + String feature_name = vformat("screen/support_%s", sizes[i]); + String feature_support = bool_to_string(p_preset->get(feature_name)); + String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support); + manifest_screen_sizes += xml_entry; + } + manifest_screen_sizes += " />\n"; + return manifest_screen_sizes; +} + +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; + if (uses_xr) { + int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof + if (dof_index == 1) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"false\" android:version=\"1\" />\n"; + } else if (dof_index == 2) { + 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) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; + } else if (hand_tracking_index == 2) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" 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_plugins_tag(const String &plugins_names) { + if (!plugins_names.empty()) { + return vformat(" <meta-data tools:node=\"replace\" android:name=\"plugins\" android:value=\"%s\" />\n", plugins_names); + } else { + return " <meta-data tools:node=\"remove\" android:name=\"plugins\" />\n"; + } +} + +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + String orientation = _get_android_orientation_label(_get_screen_orientation()); + String manifest_activity_text = vformat( + " <activity android:name=\"com.godot.game.GodotApp\" " + "tools:replace=\"android:screenOrientation\" " + "android:screenOrientation=\"%s\">\n", + orientation); + if (uses_xr) { + String focus_awareness = bool_to_string(p_preset->get("xr_features/focus_awareness")); + manifest_activity_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"%s\" />\n", focus_awareness); + } else { + manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; + } + manifest_activity_text += " </activity>\n"; + return manifest_activity_text; +} + +String _get_application_tag(const Ref<EditorExportPreset> &p_preset, const String &plugins_names) { + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + String manifest_application_text = + " <application android:label=\"@string/godot_project_name_string\"\n" + " android:allowBackup=\"false\" tools:ignore=\"GoogleAppIndexingWarning\"\n" + " android:icon=\"@mipmap/icon\">\n\n" + " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n"; + + manifest_application_text += _get_plugins_tag(plugins_names); + if (uses_xr) { + manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n"; + } + manifest_application_text += _get_activity_tag(p_preset); + manifest_application_text += " </application>\n"; + return manifest_application_text; +} + +#endif //GODOT_GRADLE_EXPORT_UTIL_H diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index 05d5fb576d..2446ca2829 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -29,12 +29,11 @@ /*************************************************************************/ #include "file_access_android.h" -#include "core/print_string.h" +#include "core/string/print_string.h" AAssetManager *FileAccessAndroid::asset_manager = nullptr; /*void FileAccessAndroid::make_default() { - create_func=create_android; }*/ diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 48c09552c1..e94681659c 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -38,7 +38,7 @@ <activity android:name=".GodotApp" android:label="@string/godot_project_name_string" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" + android:theme="@style/GodotAppSplashTheme" android:launchMode="singleTask" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 19202d2310..53d11fda5b 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -70,8 +70,8 @@ android { buildToolsVersion versions.buildTools compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion } defaultConfig { @@ -80,8 +80,15 @@ android { ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~" } + ndk { + String[] export_abi_list = getExportEnabledABIs() + abiFilters export_abi_list + } + // Feel free to modify the application id to your own. applicationId getExportPackageName() + versionCode getExportVersionCode() + versionName getExportVersionName() minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk } @@ -99,10 +106,41 @@ android { // doNotStrip '**/*.so' } - // Both signing and zip-aligning will be done at export time - buildTypes.all { buildType -> - buildType.zipAlignEnabled false - buildType.signingConfig null + signingConfigs { + release { + File keystoreFile = new File(getReleaseKeystoreFile()) + if (keystoreFile.isFile()) { + storeFile keystoreFile + storePassword getReleaseKeystorePassword() + keyAlias getReleaseKeyAlias() + keyPassword getReleaseKeystorePassword() + } + } + } + + buildTypes { + + 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. + zipAlignEnabled shouldZipAlign() + if (shouldSign()) { + signingConfig signingConfigs.release + } else { + signingConfig null + } + } } sourceSets { @@ -123,3 +161,27 @@ android { } } } + +task copyAndRenameDebugApk(type: Copy) { + from "$buildDir/outputs/apk/debug/android_debug.apk" + into getExportPath() + rename "android_debug.apk", getExportFilename() +} + +task copyAndRenameReleaseApk(type: Copy) { + from "$buildDir/outputs/apk/release/android_release.apk" + into getExportPath() + rename "android_release.apk", getExportFilename() +} + +task copyAndRenameDebugAab(type: Copy) { + from "$buildDir/outputs/bundle/debug/build-debug.aab" + into getExportPath() + rename "build-debug.aab", getExportFilename() +} + +task copyAndRenameReleaseAab(type: Copy) { + from "$buildDir/outputs/bundle/release/build-release.aab" + into getExportPath() + rename "build-release.aab", getExportFilename() +} diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index acfdef531e..80cf6f7ede 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,12 +1,13 @@ ext.versions = [ - androidGradlePlugin: '3.5.3', + androidGradlePlugin: '4.1.0', compileSdk : 29, minSdk : 18, targetSdk : 29, - buildTools : '29.0.3', + buildTools : '30.0.1', supportCoreUtils : '1.0.0', - kotlinVersion : '1.3.61', - v4Support : '1.0.0' + kotlinVersion : '1.4.10', + v4Support : '1.0.0', + javaVersion : 1.8 ] @@ -28,8 +29,59 @@ ext.getExportPackageName = { -> return appId } +ext.getExportVersionCode = { -> + String versionCode = project.hasProperty("export_version_code") ? project.property("export_version_code") : "" + if (versionCode == null || versionCode.isEmpty()) { + versionCode = "1" + } + try { + return Integer.parseInt(versionCode) + } catch (NumberFormatException ignored) { + return 1 + } +} + +ext.getExportVersionName = { -> + String versionName = project.hasProperty("export_version_name") ? project.property("export_version_name") : "" + if (versionName == null || versionName.isEmpty()) { + versionName = "1.0" + } + return versionName +} + final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|" +// get the list of ABIs the project should be exported to +ext.getExportEnabledABIs = { -> + String enabledABIs = project.hasProperty("export_enabled_abis") ? project.property("export_enabled_abis") : ""; + if (enabledABIs == null || enabledABIs.isEmpty()) { + enabledABIs = "armeabi-v7a|arm64-v8a|x86|x86_64|" + } + Set<String> exportAbiFilter = []; + for (String abi_name : enabledABIs.split(PLUGIN_VALUE_SEPARATOR_REGEX)) { + if (!abi_name.trim().isEmpty()){ + exportAbiFilter.add(abi_name); + } + } + return exportAbiFilter; +} + +ext.getExportPath = { + String exportPath = project.hasProperty("export_path") ? project.property("export_path") : "" + if (exportPath == null || exportPath.isEmpty()) { + exportPath = "." + } + return exportPath +} + +ext.getExportFilename = { + String exportFilename = project.hasProperty("export_filename") ? project.property("export_filename") : "" + if (exportFilename == null || exportFilename.isEmpty()) { + exportFilename = "godot_android" + } + return exportFilename +} + /** * Parse the project properties for the 'plugins_maven_repos' property and return the list * of maven repos. @@ -88,3 +140,37 @@ ext.getGodotPluginsLocalBinaries = { -> return binDeps } + +ext.getReleaseKeystoreFile = { -> + String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : "" + if (keystoreFile == null || keystoreFile.isEmpty()) { + keystoreFile = "." + } + return keystoreFile +} + +ext.getReleaseKeystorePassword = { -> + String keystorePassword = project.hasProperty("release_keystore_password") ? project.property("release_keystore_password") : "" + return keystorePassword +} + +ext.getReleaseKeyAlias = { -> + String keyAlias = project.hasProperty("release_keystore_alias") ? project.property("release_keystore_alias") : "" + return keyAlias +} + +ext.shouldZipAlign = { -> + String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : "" + if (zipAlignFlag == null || zipAlignFlag.isEmpty()) { + zipAlignFlag = "false" + } + return Boolean.parseBoolean(zipAlignFlag) +} + +ext.shouldSign = { -> + String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : "" + if (signFlag == null || signFlag.isEmpty()) { + signFlag = "false" + } + return Boolean.parseBoolean(signFlag) +} diff --git a/platform/android/java/app/res/drawable/splash.png b/platform/android/java/app/res/drawable/splash.png Binary files differnew file mode 100644 index 0000000000..7bddd4325a --- /dev/null +++ b/platform/android/java/app/res/drawable/splash.png diff --git a/platform/android/java/app/res/drawable/splash_bg_color.png b/platform/android/java/app/res/drawable/splash_bg_color.png Binary files differnew file mode 100644 index 0000000000..004b6fd508 --- /dev/null +++ b/platform/android/java/app/res/drawable/splash_bg_color.png diff --git a/platform/android/java/app/res/drawable/splash_drawable.xml b/platform/android/java/app/res/drawable/splash_drawable.xml new file mode 100644 index 0000000000..2794a40817 --- /dev/null +++ b/platform/android/java/app/res/drawable/splash_drawable.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/splash_bg_color" /> + + <item> + <bitmap + android:gravity="center" + android:src="@drawable/splash" /> + </item> + +</layer-list> diff --git a/platform/android/java/lib/res/values-ar/strings.xml b/platform/android/java/app/res/values-ar/godot_project_name_string.xml index 9f3dc6d6ac..23aa5cf3e1 100644 --- a/platform/android/java/lib/res/values-ar/strings.xml +++ b/platform/android/java/app/res/values-ar/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-ar</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-bg/strings.xml b/platform/android/java/app/res/values-bg/godot_project_name_string.xml index bd8109277e..dbb7e04ae5 100644 --- a/platform/android/java/lib/res/values-bg/strings.xml +++ b/platform/android/java/app/res/values-bg/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-bg</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-ca/strings.xml b/platform/android/java/app/res/values-ca/godot_project_name_string.xml index 494cb88468..709d0961e6 100644 --- a/platform/android/java/lib/res/values-ca/strings.xml +++ b/platform/android/java/app/res/values-ca/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-ca</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-cs/strings.xml b/platform/android/java/app/res/values-cs/godot_project_name_string.xml index 30ce00f895..ab248a8032 100644 --- a/platform/android/java/lib/res/values-cs/strings.xml +++ b/platform/android/java/app/res/values-cs/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-cs</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-da/strings.xml b/platform/android/java/app/res/values-da/godot_project_name_string.xml index 4c2a1cf0f4..906bf44f57 100644 --- a/platform/android/java/lib/res/values-da/strings.xml +++ b/platform/android/java/app/res/values-da/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-da</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-de/strings.xml b/platform/android/java/app/res/values-de/godot_project_name_string.xml index 52946d4cce..0cacb0175f 100644 --- a/platform/android/java/lib/res/values-de/strings.xml +++ b/platform/android/java/app/res/values-de/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-de</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-el/strings.xml b/platform/android/java/app/res/values-el/godot_project_name_string.xml index 181dc51762..047de616a5 100644 --- a/platform/android/java/lib/res/values-el/strings.xml +++ b/platform/android/java/app/res/values-el/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-el</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-en/strings.xml b/platform/android/java/app/res/values-en/godot_project_name_string.xml index 976a565013..bb3a5dbef3 100644 --- a/platform/android/java/lib/res/values-en/strings.xml +++ b/platform/android/java/app/res/values-en/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-en</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-es-rES/strings.xml b/platform/android/java/app/res/values-es-rES/godot_project_name_string.xml index 73f63a08f8..d4537f3496 100644 --- a/platform/android/java/lib/res/values-es-rES/strings.xml +++ b/platform/android/java/app/res/values-es-rES/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-es_ES</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-es/strings.xml b/platform/android/java/app/res/values-es/godot_project_name_string.xml index 07b718a641..d63a16022e 100644 --- a/platform/android/java/lib/res/values-es/strings.xml +++ b/platform/android/java/app/res/values-es/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-es</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/app/res/values-fa/godot_project_name_string.xml b/platform/android/java/app/res/values-fa/godot_project_name_string.xml new file mode 100644 index 0000000000..c303f13d5f --- /dev/null +++ b/platform/android/java/app/res/values-fa/godot_project_name_string.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">godot-project-name-fa</string> +</resources> diff --git a/platform/android/java/lib/res/values-fi/strings.xml b/platform/android/java/app/res/values-fi/godot_project_name_string.xml index 323d82aff1..bd6005574a 100644 --- a/platform/android/java/lib/res/values-fi/strings.xml +++ b/platform/android/java/app/res/values-fi/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-fi</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-fr/strings.xml b/platform/android/java/app/res/values-fr/godot_project_name_string.xml index 32bead2661..2e94b65a20 100644 --- a/platform/android/java/lib/res/values-fr/strings.xml +++ b/platform/android/java/app/res/values-fr/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-fr</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-hi/strings.xml b/platform/android/java/app/res/values-hi/godot_project_name_string.xml index 8aab2a8c63..0bf75dcd56 100644 --- a/platform/android/java/lib/res/values-hi/strings.xml +++ b/platform/android/java/app/res/values-hi/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-hi</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-hr/strings.xml b/platform/android/java/app/res/values-hr/godot_project_name_string.xml index caf55e2241..d3f75910f9 100644 --- a/platform/android/java/lib/res/values-hr/strings.xml +++ b/platform/android/java/app/res/values-hr/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-hr</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-hu/strings.xml b/platform/android/java/app/res/values-hu/godot_project_name_string.xml index e7f9e51226..012b613af3 100644 --- a/platform/android/java/lib/res/values-hu/strings.xml +++ b/platform/android/java/app/res/values-hu/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-hu</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/app/res/values-in/godot_project_name_string.xml b/platform/android/java/app/res/values-in/godot_project_name_string.xml new file mode 100644 index 0000000000..eedecff7a1 --- /dev/null +++ b/platform/android/java/app/res/values-in/godot_project_name_string.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">godot-project-name-in</string> +</resources> diff --git a/platform/android/java/lib/res/values-it/strings.xml b/platform/android/java/app/res/values-it/godot_project_name_string.xml index 1f5e5a049e..7e734047c4 100644 --- a/platform/android/java/lib/res/values-it/strings.xml +++ b/platform/android/java/app/res/values-it/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-it</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/app/res/values-iw/godot_project_name_string.xml b/platform/android/java/app/res/values-iw/godot_project_name_string.xml new file mode 100644 index 0000000000..03893f0cbb --- /dev/null +++ b/platform/android/java/app/res/values-iw/godot_project_name_string.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">godot-project-name-iw</string> +</resources> diff --git a/platform/android/java/lib/res/values-ja/strings.xml b/platform/android/java/app/res/values-ja/godot_project_name_string.xml index 7f85f57df7..f9dd4fab0d 100644 --- a/platform/android/java/lib/res/values-ja/strings.xml +++ b/platform/android/java/app/res/values-ja/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-ja</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/app/res/values-ko/godot_project_name_string.xml b/platform/android/java/app/res/values-ko/godot_project_name_string.xml new file mode 100644 index 0000000000..26f5dac176 --- /dev/null +++ b/platform/android/java/app/res/values-ko/godot_project_name_string.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">godot-project-name-ko</string> +</resources> diff --git a/platform/android/java/lib/res/values-lt/strings.xml b/platform/android/java/app/res/values-lt/godot_project_name_string.xml index 6e3677fde7..1c2e976cc5 100644 --- a/platform/android/java/lib/res/values-lt/strings.xml +++ b/platform/android/java/app/res/values-lt/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-lt</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-lv/strings.xml b/platform/android/java/app/res/values-lv/godot_project_name_string.xml index 701fc271ac..b5e638ed73 100644 --- a/platform/android/java/lib/res/values-lv/strings.xml +++ b/platform/android/java/app/res/values-lv/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-lv</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-nb/strings.xml b/platform/android/java/app/res/values-nb/godot_project_name_string.xml index 73147ca1af..e6d89d6a3f 100644 --- a/platform/android/java/lib/res/values-nb/strings.xml +++ b/platform/android/java/app/res/values-nb/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-nb</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-nl/strings.xml b/platform/android/java/app/res/values-nl/godot_project_name_string.xml index e501928a35..93cb3a3878 100644 --- a/platform/android/java/lib/res/values-nl/strings.xml +++ b/platform/android/java/app/res/values-nl/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-nl</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-pl/strings.xml b/platform/android/java/app/res/values-pl/godot_project_name_string.xml index ea5da73b6f..e5d6ac74fb 100644 --- a/platform/android/java/lib/res/values-pl/strings.xml +++ b/platform/android/java/app/res/values-pl/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-pl</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-pt/strings.xml b/platform/android/java/app/res/values-pt/godot_project_name_string.xml index bdda7cd2c7..a4624655c5 100644 --- a/platform/android/java/lib/res/values-pt/strings.xml +++ b/platform/android/java/app/res/values-pt/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-pt</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-ro/strings.xml b/platform/android/java/app/res/values-ro/godot_project_name_string.xml index 3686da4c19..19e026637e 100644 --- a/platform/android/java/lib/res/values-ro/strings.xml +++ b/platform/android/java/app/res/values-ro/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-ro</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-ru/strings.xml b/platform/android/java/app/res/values-ru/godot_project_name_string.xml index 954067658b..284845241f 100644 --- a/platform/android/java/lib/res/values-ru/strings.xml +++ b/platform/android/java/app/res/values-ru/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-ru</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-sk/strings.xml b/platform/android/java/app/res/values-sk/godot_project_name_string.xml index 37d1283124..f8ab4a5b59 100644 --- a/platform/android/java/lib/res/values-sk/strings.xml +++ b/platform/android/java/app/res/values-sk/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-sk</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-sl/strings.xml b/platform/android/java/app/res/values-sl/godot_project_name_string.xml index 0bb249c375..98bd53e8d2 100644 --- a/platform/android/java/lib/res/values-sl/strings.xml +++ b/platform/android/java/app/res/values-sl/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-sl</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-sr/strings.xml b/platform/android/java/app/res/values-sr/godot_project_name_string.xml index 0e83cab1a1..3f400f2a4d 100644 --- a/platform/android/java/lib/res/values-sr/strings.xml +++ b/platform/android/java/app/res/values-sr/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-sr</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-sv/strings.xml b/platform/android/java/app/res/values-sv/godot_project_name_string.xml index e3a04ac2ec..8670b7c9aa 100644 --- a/platform/android/java/lib/res/values-sv/strings.xml +++ b/platform/android/java/app/res/values-sv/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-sv</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-th/strings.xml b/platform/android/java/app/res/values-th/godot_project_name_string.xml index 0aa893b8bf..a1cc1bcd49 100644 --- a/platform/android/java/lib/res/values-th/strings.xml +++ b/platform/android/java/app/res/values-th/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-th</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-tl/strings.xml b/platform/android/java/app/res/values-tl/godot_project_name_string.xml index e7e2af4909..6d66d114cf 100644 --- a/platform/android/java/lib/res/values-tl/strings.xml +++ b/platform/android/java/app/res/values-tl/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-tl</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-tr/strings.xml b/platform/android/java/app/res/values-tr/godot_project_name_string.xml index 97af1243a6..ba3bd7de36 100644 --- a/platform/android/java/lib/res/values-tr/strings.xml +++ b/platform/android/java/app/res/values-tr/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-tr</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-uk/strings.xml b/platform/android/java/app/res/values-uk/godot_project_name_string.xml index 3dea6908a9..5f14ab25a0 100644 --- a/platform/android/java/lib/res/values-uk/strings.xml +++ b/platform/android/java/app/res/values-uk/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-uk</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-vi/strings.xml b/platform/android/java/app/res/values-vi/godot_project_name_string.xml index a6552130b0..295378e111 100644 --- a/platform/android/java/lib/res/values-vi/strings.xml +++ b/platform/android/java/app/res/values-vi/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-vi</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values-zh-rHK/strings.xml b/platform/android/java/app/res/values-zh-rHK/godot_project_name_string.xml index 8a6269da0f..40ab0f285a 100644 --- a/platform/android/java/lib/res/values-zh-rHK/strings.xml +++ b/platform/android/java/app/res/values-zh-rHK/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-zh_HK</string> </resources> diff --git a/platform/android/java/lib/res/values-zh-rTW/strings.xml b/platform/android/java/app/res/values-zh-rTW/godot_project_name_string.xml index b1bb39d5d6..095bd564e2 100644 --- a/platform/android/java/lib/res/values-zh-rTW/strings.xml +++ b/platform/android/java/app/res/values-zh-rTW/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-zh_TW</string> </resources> diff --git a/platform/android/java/lib/res/values-zh-rCN/strings.xml b/platform/android/java/app/res/values-zh/godot_project_name_string.xml index 6668c56bd9..31aa8c273a 100644 --- a/platform/android/java/lib/res/values-zh-rCN/strings.xml +++ b/platform/android/java/app/res/values-zh/godot_project_name_string.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> <resources> <string name="godot_project_name_string">godot-project-name-zh</string> </resources> diff --git a/platform/android/java/app/res/values/godot_project_name_string.xml b/platform/android/java/app/res/values/godot_project_name_string.xml new file mode 100644 index 0000000000..7ec2738896 --- /dev/null +++ b/platform/android/java/app/res/values/godot_project_name_string.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME--> +<resources> + <string name="godot_project_name_string">godot-project-name</string> +</resources> diff --git a/platform/android/java/app/res/values/themes.xml b/platform/android/java/app/res/values/themes.xml new file mode 100644 index 0000000000..99f723f5ba --- /dev/null +++ b/platform/android/java/app/res/values/themes.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"/> + + <style name="GodotAppSplashTheme" parent="@style/GodotAppMainTheme"> + <item name="android:windowBackground">@drawable/splash_drawable</item> + <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> + </style> +</resources> 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 1af5950cbe..51df70969e 100644 --- a/platform/android/java/app/src/com/godot/game/GodotApp.java +++ b/platform/android/java/app/src/com/godot/game/GodotApp.java @@ -32,9 +32,16 @@ package com.godot.game; import org.godotengine.godot.FullScreenGodotApp; +import android.os.Bundle; + /** * Template activity for Godot Android custom builds. * Feel free to extend and modify this class for your custom logic. */ public class GodotApp extends FullScreenGodotApp { + @Override + public void onCreate(Bundle savedInstanceState) { + setTheme(R.style.GodotAppMainTheme); + super.onCreate(savedInstanceState); + } } diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 821a4dc584..73c136ed0e 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -22,8 +22,6 @@ allprojects { } ext { - sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : "" - supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"] supportedTargets = ["release", "debug"] diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties index e14cd5ba5c..2dc069ad2f 100644 --- a/platform/android/java/gradle.properties +++ b/platform/android/java/gradle.properties @@ -18,3 +18,5 @@ org.gradle.jvmargs=-Xmx1536m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true + +org.gradle.warning.mode=all diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index f56b0f6a5e..a7d8a0f310 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 19eee5a315..89ce3d15e6 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -18,6 +18,11 @@ android { targetSdkVersion versions.targetSdk } + compileOptions { + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion + } + lintOptions { abortOnError false disable 'MissingTranslation', 'UnusedResources' @@ -50,15 +55,6 @@ android { def buildType = variant.buildType.name.capitalize() - def taskPrefix = "" - if (project.path != ":") { - taskPrefix = project.path + ":" - } - - // Disable the externalNativeBuild* task as it would cause build failures since the cmake build - // files is only setup for editing support. - gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType - def releaseTarget = buildType.toLowerCase() if (releaseTarget == null || releaseTarget == "") { throw new GradleException("Invalid build type: " + buildType) @@ -68,20 +64,46 @@ android { throw new GradleException("Invalid default abi: " + defaultAbi) } + // Find scons' executable path + File sconsExecutableFile = null + def sconsName = "scons" + def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows() + ? [".bat", ".exe"] + : [""]) + logger.lifecycle("Looking for $sconsName executable path") + 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()) { + // Pick the first option and we're done! + sconsExecutableFile = allOptions.get(0) + 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 "scons" + sconsExt + executable sconsExecutableFile.absolutePath args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors() } // Schedule the tasks so the generated libs are present before the aar file is packaged. tasks["merge${buildType}JniLibFolders"].dependsOn taskName } - - externalNativeBuild { - cmake { - path "CMakeLists.txt" - } - } } diff --git a/platform/android/java/lib/res/layout/downloading_expansion.xml b/platform/android/java/lib/res/layout/downloading_expansion.xml index 4a9700965f..34c2757598 100644 --- a/platform/android/java/lib/res/layout/downloading_expansion.xml +++ b/platform/android/java/lib/res/layout/downloading_expansion.xml @@ -162,4 +162,4 @@ </LinearLayout> </LinearLayout> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml b/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml index fae1faeb60..426e1bd841 100644 --- a/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml +++ b/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml @@ -105,4 +105,4 @@ </RelativeLayout> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml index 1ed4037035..cfdcca2ab5 100644 --- a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml +++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml @@ -2,4 +2,4 @@ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="@mipmap/icon_background"/> <foreground android:drawable="@mipmap/icon_foreground"/> -</adaptive-icon>
\ No newline at end of file +</adaptive-icon> diff --git a/platform/android/java/lib/res/values-fa/strings.xml b/platform/android/java/lib/res/values-fa/strings.xml index f1e29013c4..60b01accf1 100644 --- a/platform/android/java/lib/res/values-fa/strings.xml +++ b/platform/android/java/lib/res/values-fa/strings.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="godot_project_name_string">godot-project-name-fa</string> <string name="text_paused_cellular">آیا می خواهید بر روی اتصال داده همراه دانلود را شروع کنید؟ بر اساس نوع سطح داده شما این ممکن است برای شما هزینه مالی داشته باشد.</string> <string name="text_paused_cellular_2">اگر نمی خواهید بر روی اتصال داده همراه دانلود را شروع کنید ، دانلود به صورت خودکار در زمان دسترسی به وای-فای شروع می شود.</string> <string name="text_button_resume_cellular">ادامه دانلود</string> diff --git a/platform/android/java/lib/res/values-in/strings.xml b/platform/android/java/lib/res/values-in/strings.xml deleted file mode 100644 index 9e9a8b0c03..0000000000 --- a/platform/android/java/lib/res/values-in/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="godot_project_name_string">godot-project-name-id</string> -</resources>
\ No newline at end of file diff --git a/platform/android/java/lib/res/values-iw/strings.xml b/platform/android/java/lib/res/values-iw/strings.xml deleted file mode 100644 index f52ede2085..0000000000 --- a/platform/android/java/lib/res/values-iw/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="godot_project_name_string">godot-project-name-he</string> -</resources>
\ No newline at end of file diff --git a/platform/android/java/lib/res/values-ko/strings.xml b/platform/android/java/lib/res/values-ko/strings.xml index fab0bdd753..7b62345977 100644 --- a/platform/android/java/lib/res/values-ko/strings.xml +++ b/platform/android/java/lib/res/values-ko/strings.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="godot_project_name_string">godot-project-name-ko</string> <string name="text_paused_cellular">모바일 네트워크를 사용하여 다운로드 하시겠습니까? 남은 데이터 사용량에 따라, 요금이 부과될 수 있습니다.</string> <string name="text_paused_cellular_2">모바일 네트워크를 사용하여 다운로드 하지 않을 경우, 와이파이 연결이 가능할 때 자동적으로 다운로드가 이루어집니다.</string> <string name="text_button_resume_cellular">다운로드 계속하기</string> @@ -52,4 +51,4 @@ <string name="kilobytes_per_second">%1$s KB/s</string> <string name="time_remaining">남은 시간: %1$s</string> <string name="time_remaining_notification">%1$s 남음</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values/dimens.xml b/platform/android/java/lib/res/values/dimens.xml new file mode 100644 index 0000000000..9034dbbcc1 --- /dev/null +++ b/platform/android/java/lib/res/values/dimens.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="text_edit_height">48dp</dimen> +</resources> diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml index a1b81a6186..590b066d8a 100644 --- a/platform/android/java/lib/res/values/strings.xml +++ b/platform/android/java/lib/res/values/strings.xml @@ -52,4 +52,4 @@ <string name="kilobytes_per_second">%1$s KB/s</string> <string name="time_remaining">Time remaining: %1$s</string> <string name="time_remaining_notification">%1$s left</string> -</resources>
\ No newline at end of file +</resources> diff --git a/platform/android/java/lib/res/values/styles.xml b/platform/android/java/lib/res/values/styles.xml index a442f61e7e..b798373bc6 100644 --- a/platform/android/java/lib/res/values/styles.xml +++ b/platform/android/java/lib/res/values/styles.xml @@ -22,4 +22,4 @@ <item name="android:background">@android:color/background_dark</item> </style> -</resources>
\ No newline at end of file +</resources> 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 138c2de94c..5aa48d87da 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -34,6 +34,8 @@ import android.content.Intent; import android.os.Bundle; import android.view.KeyEvent; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; /** @@ -43,13 +45,18 @@ import androidx.fragment.app.FragmentActivity; * within an Android app. */ public abstract class FullScreenGodotApp extends FragmentActivity { - protected Godot godotFragment; + @Nullable + private Godot godotFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.godot_app_layout); - godotFragment = new Godot(); + 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(); } @@ -76,4 +83,17 @@ public abstract class FullScreenGodotApp extends FragmentActivity { } return super.onKeyMultiple(inKeyCode, repeatCount, event); } + + /** + * Used to initialize the Godot fragment instance in {@link FullScreenGodotApp#onCreate(Bundle)}. + */ + @NonNull + protected Godot initGodotInstance() { + return new Godot(); + } + + @Nullable + protected final Godot getGodotFragment() { + return godotFragment; + } } 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 1b55090451..3bbe35091c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -70,6 +70,7 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings.Secure; import android.view.Display; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -151,8 +152,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC private void setButtonPausedState(boolean paused) { mStatePaused = paused; - int stringResourceID = paused ? R.string.text_button_resume : - R.string.text_button_pause; + int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause; mPauseButton.setText(stringResourceID); } @@ -221,7 +221,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC // GodotEditText layout GodotEditText editText = new GodotEditText(activity); - editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, + (int)getResources().getDimension(R.dimen.text_edit_height))); // ...add to FrameLayout containerLayout.addView(editText); @@ -759,9 +760,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC /* @Override public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) { - System.out.printf("** BACK REQUEST!\n"); GodotLib.quit(); @@ -854,63 +853,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } } - public boolean gotTouchEvent(final MotionEvent event) { - final int evcount = event.getPointerCount(); - if (evcount == 0) - return true; - - if (mRenderView != null) { - final int[] arr = new int[event.getPointerCount() * 3]; - - for (int i = 0; i < event.getPointerCount(); i++) { - arr[i * 3 + 0] = (int)event.getPointerId(i); - arr[i * 3 + 1] = (int)event.getX(i); - arr[i * 3 + 2] = (int)event.getY(i); - } - final int pointer_idx = event.getPointerId(event.getActionIndex()); - - //System.out.printf("gaction: %d\n",event.getAction()); - final int action = event.getAction() & MotionEvent.ACTION_MASK; - mRenderView.queueOnRenderThread(new Runnable() { - @Override - public void run() { - switch (action) { - case MotionEvent.ACTION_DOWN: { - GodotLib.touch(0, 0, evcount, arr); - //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); - } break; - case MotionEvent.ACTION_MOVE: { - GodotLib.touch(1, 0, evcount, arr); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); - } - */ - } break; - case MotionEvent.ACTION_POINTER_UP: { - GodotLib.touch(4, pointer_idx, evcount, arr); - //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case MotionEvent.ACTION_POINTER_DOWN: { - GodotLib.touch(3, pointer_idx, evcount, arr); - //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: { - GodotLib.touch(2, 0, evcount, arr); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); - } - */ - } break; - } - } - }); - } - return true; - } - public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { String s = event.getCharacters(); if (s == null || s.length() == 0) 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 4da2f31250..d731e080c4 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -29,7 +29,6 @@ /*************************************************************************/ package org.godotengine.godot; - import org.godotengine.godot.input.GodotGestureHandler; import org.godotengine.godot.input.GodotInputHandler; import org.godotengine.godot.utils.GLUtils; @@ -117,12 +116,17 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView godot.onBackPressed(); } + @Override + public GodotInputHandler getInputHandler() { + return inputHandler; + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); this.detector.onTouchEvent(event); - return godot.gotTouchEvent(event); + return inputHandler.onTouchEvent(event); } @Override 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 4dd228e53b..894009e30f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -37,12 +37,16 @@ import android.content.*; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.AssetManager; +import android.graphics.Point; import android.media.*; import android.net.Uri; import android.os.*; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.WindowInsets; import java.io.IOException; import java.io.InputStream; @@ -461,9 +465,31 @@ public class GodotIO { return (int)(metrics.density * 160f); } - public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + public int[] screenGetUsableRect() { + DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + Display display = activity.getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getRealSize(size); + + int result[] = { 0, 0, size.x, size.y }; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets(); + DisplayCutout cutout = insets.getDisplayCutout(); + if (cutout != null) { + int insetLeft = cutout.getSafeInsetLeft(); + int insetTop = cutout.getSafeInsetTop(); + result[0] = insetLeft; + result[1] = insetTop; + result[2] -= insetLeft + cutout.getSafeInsetRight(); + result[3] -= insetTop + cutout.getSafeInsetBottom(); + } + } + 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_max_input_length, p_cursor_start, p_cursor_end); + edit.showKeyboard(p_existing_text, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); //inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); @@ -489,13 +515,13 @@ public class GodotIO { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); } break; case SCREEN_SENSOR_LANDSCAPE: { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE); } break; case SCREEN_SENSOR_PORTRAIT: { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); } break; case SCREEN_SENSOR: { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } break; } } 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 318e2816ff..6ccbe91e60 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -94,17 +94,19 @@ public class GodotLib { /** * Forward touch events from the main thread to the GL thread. */ - public static native void touch(int what, int pointer, int howmany, int[] arr); + public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions); + public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask); + public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor); /** * Forward hover events from the main thread to the GL thread. */ - public static native void hover(int type, int x, int y); + public static native void hover(int type, float x, float y); /** * Forward double_tap events from the main thread to the GL thread. */ - public static native void doubletap(int x, int y); + public static native void doubleTap(int buttonMask, int x, int y); /** * Forward scroll events from the main thread to the GL thread. diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 27e63f3a66..68b8a16641 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -30,6 +30,8 @@ package org.godotengine.godot; +import org.godotengine.godot.input.GodotInputHandler; + import android.view.SurfaceView; public interface GodotRenderView { @@ -43,4 +45,6 @@ public interface GodotRenderView { abstract public void onActivityResumed(); abstract public void onBackPressed(); + + abstract public GodotInputHandler getInputHandler(); } 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 aace593bae..6cd5ca7b4e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -38,6 +38,7 @@ import org.godotengine.godot.vulkan.VkSurfaceView; import android.annotation.SuppressLint; import android.content.Context; import android.view.GestureDetector; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceView; @@ -90,27 +91,32 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV godot.onBackPressed(); } + @Override + public GodotInputHandler getInputHandler() { + return mInputHandler; + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); mGestureDetector.onTouchEvent(event); - return godot.gotTouchEvent(event); + return mInputHandler.onTouchEvent(event); } @Override public boolean onKeyUp(final int keyCode, KeyEvent event) { - return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + return mInputHandler.onKeyUp(keyCode, event); } @Override public boolean onKeyDown(final int keyCode, KeyEvent event) { - return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + return mInputHandler.onKeyDown(keyCode, event); } @Override public boolean onGenericMotionEvent(MotionEvent event) { - return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); + return mInputHandler.onGenericMotionEvent(event); } @Override 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 7f596575a8..c95339c583 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 @@ -36,6 +36,7 @@ import android.content.Context; import android.os.Handler; import android.os.Message; import android.text.InputFilter; +import android.text.InputType; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; @@ -58,7 +59,8 @@ public class GodotEditText extends EditText { private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; - private int mMaxInputLength; + private int mMaxInputLength = Integer.MAX_VALUE; + private boolean mMultiline = false; private static class EditHandler extends Handler { private final WeakReference<GodotEditText> mEdit; @@ -95,7 +97,11 @@ public class GodotEditText extends EditText { protected void initView() { setPadding(0, 0, 0, 0); - setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE); + } + + public boolean isMultiline() { + return mMultiline; } private void handleMessage(final Message msg) { @@ -115,6 +121,12 @@ public class GodotEditText extends EditText { edit.mInputWrapper.setSelection(false); } + int inputType = InputType.TYPE_CLASS_TEXT; + if (edit.isMultiline()) { + inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; + } + edit.setInputType(inputType); + edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -155,20 +167,41 @@ public class GodotEditText extends EditText { // =========================================================== @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - super.onKeyDown(keyCode, keyEvent); - - /* Let GlSurfaceView get focus if back key is input. */ + /* Let SurfaceView get focus if back key is input. */ if (keyCode == KeyEvent.KEYCODE_BACK) { mRenderView.getView().requestFocus(); } - return true; + // pass event to godot in special cases + if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyDown(keyCode, keyEvent)) { + return true; + } else { + return super.onKeyDown(keyCode, keyEvent); + } + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { + if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent)) { + return true; + } else { + return super.onKeyUp(keyCode, keyEvent); + } + } + + 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; + boolean isModifiedKey = keyEvent.isAltPressed() || keyEvent.isCtrlPressed() || keyEvent.isSymPressed() || + keyEvent.isFunctionPressed() || keyEvent.isMetaPressed(); + return isArrowKey || keyCode == KeyEvent.KEYCODE_TAB || KeyEvent.isModifierKey(keyCode) || + isModifiedKey; } // =========================================================== // Methods // =========================================================== - public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { int maxInputLength = (p_max_input_length <= 0) ? Integer.MAX_VALUE : p_max_input_length; if (p_cursor_start == -1) { // cursor position not given this.mOriginText = p_existing_text; @@ -181,6 +214,8 @@ public class GodotEditText extends EditText { this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end); } + this.mMultiline = p_multiline; + final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; msg.obj = this; 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 1c9a683bbd..fb151fa504 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 @@ -33,7 +33,6 @@ package org.godotengine.godot.input; import org.godotengine.godot.GodotLib; import org.godotengine.godot.GodotRenderView; -import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; @@ -75,10 +74,11 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener //Log.i("GodotGesture", "onDoubleTap"); final int x = Math.round(event.getX()); final int y = Math.round(event.getY()); + final int buttonMask = event.getButtonState(); queueEvent(new Runnable() { @Override public void run() { - GodotLib.doubletap(x, y); + GodotLib.doubleTap(buttonMask, x, y); } }); 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 9abd65cc67..f3e985f944 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 @@ -36,6 +36,7 @@ import org.godotengine.godot.GodotLib; import org.godotengine.godot.GodotRenderView; import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; +import android.os.Build; import android.util.Log; import android.view.InputDevice; import android.view.InputDevice.MotionRange; @@ -156,6 +157,53 @@ public class GodotInputHandler implements InputDeviceListener { return true; } + public boolean onTouchEvent(final MotionEvent event) { + // Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + if (event.getAction() != MotionEvent.ACTION_MOVE) { + // we return true because every time a mouse event is fired, the event is already handled + // in onGenericMotionEvent, so by touch event we can say that the event is also handled + return true; + } + return handleMouseEvent(event); + } + + final int evcount = event.getPointerCount(); + if (evcount == 0) + return true; + + if (mRenderView != null) { + final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc... + + for (int i = 0; i < event.getPointerCount(); i++) { + arr[i * 3 + 0] = event.getPointerId(i); + arr[i * 3 + 1] = event.getX(i); + arr[i * 3 + 2] = event.getY(i); + } + final int action = event.getActionMasked(); + + mRenderView.queueOnRenderThread(new Runnable() { + @Override + public void run() { + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_MOVE: { + GodotLib.touch(event.getSource(), action, 0, evcount, arr); + } break; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_POINTER_DOWN: { + int pointer_idx = event.getPointerId(event.getActionIndex()); + GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr); + } break; + } + } + }); + } + return true; + } + public boolean onGenericMotionEvent(MotionEvent event) { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { final int device_id = findJoystickDevice(event.getDeviceId()); @@ -189,8 +237,8 @@ public class GodotInputHandler implements InputDeviceListener { return true; } } else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) { - final int x = Math.round(event.getX()); - final int y = Math.round(event.getY()); + final float x = event.getX(); + final float y = event.getY(); final int type = event.getAction(); queueEvent(new Runnable() { @Override @@ -199,6 +247,10 @@ public class GodotInputHandler implements InputDeviceListener { } }); return true; + } else if ((event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return handleMouseEvent(event); + } } return false; @@ -366,4 +418,53 @@ public class GodotInputHandler implements InputDeviceListener { return -1; } + + private boolean handleMouseEvent(final MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + case MotionEvent.ACTION_HOVER_EXIT: { + final float x = event.getX(); + final float y = event.getY(); + final int type = event.getAction(); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.hover(type, x, y); + } + }); + return true; + } + case MotionEvent.ACTION_BUTTON_PRESS: + case MotionEvent.ACTION_BUTTON_RELEASE: + case MotionEvent.ACTION_MOVE: { + final float x = event.getX(); + final float y = event.getY(); + final int buttonsMask = event.getButtonState(); + final int action = event.getAction(); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask); + } + }); + return true; + } + case MotionEvent.ACTION_SCROLL: { + final float x = event.getX(); + final float y = event.getY(); + final int buttonsMask = event.getButtonState(); + final int action = event.getAction(); + final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL); + final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor); + } + }); + } + } + return false; + } } 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 9c7cf9f341..4dd1054738 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 @@ -123,7 +123,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void run() { for (int i = 0; i < count; ++i) { int key = newChars[i]; - if (key == '\n') { + if ((key == '\n') && !mEdit.isMultiline()) { // Return keys are handled through action events continue; } @@ -151,7 +151,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene }); } - if (pActionID == EditorInfo.IME_NULL) { + if (pActionID == EditorInfo.IME_ACTION_DONE) { // Enter key has been pressed GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, 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 aeb4628d5d..f93cf0fa38 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 @@ -52,7 +52,6 @@ import org.godotengine.godot.plugin.GodotPluginRegistry * @see [VkSurfaceView.startRenderer] */ internal class VkRenderer { - private val pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry() /** @@ -71,7 +70,7 @@ internal class VkRenderer { */ fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) { GodotLib.resize(surface, width, height) - + for (plugin in pluginRegistry.getAllPlugins()) { plugin.onVkSurfaceChanged(surface, width, height) } 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 6b0e12b21a..e5c7a39bfb 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 @@ -50,7 +50,6 @@ import android.view.SurfaceView * </ul> */ open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback { - companion object { fun checkState(expression: Boolean, errorMessage: Any) { check(expression) { errorMessage.toString() } 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 7557c8aa22..fb02e3a69f 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 @@ -41,7 +41,6 @@ import kotlin.concurrent.withLock * The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread. */ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) { - companion object { private val TAG = VkThread::class.java.simpleName } @@ -226,5 +225,4 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk threadExiting() } } - } diff --git a/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml b/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml new file mode 100644 index 0000000000..dc180375d5 --- /dev/null +++ b/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest package="org.godotengine.godot" /> diff --git a/platform/android/java/lib/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt index d3bdf6a5f2..34925684da 100644 --- a/platform/android/java/lib/CMakeLists.txt +++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt @@ -1,3 +1,4 @@ +# Non functional cmake build file used to provide Android Studio editor support to the project. cmake_minimum_required(VERSION 3.6) project(godot) diff --git a/platform/android/java/nativeSrcsConfigs/README.md b/platform/android/java/nativeSrcsConfigs/README.md new file mode 100644 index 0000000000..e48505ccda --- /dev/null +++ b/platform/android/java/nativeSrcsConfigs/README.md @@ -0,0 +1,4 @@ +## Native sources configs + +This is a non functional Android library used to provide Android Studio editor support to the Godot project native files. +Nothing else should be added to this library. diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle new file mode 100644 index 0000000000..65b7bb9dc9 --- /dev/null +++ b/platform/android/java/nativeSrcsConfigs/build.gradle @@ -0,0 +1,54 @@ +// Non functional android library used to provide Android Studio editor support to the project. +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion versions.compileSdk + buildToolsVersion versions.buildTools + + defaultConfig { + minSdkVersion versions.minSdk + targetSdkVersion versions.targetSdk + } + + compileOptions { + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion + } + + packagingOptions { + exclude 'META-INF/LICENSE' + exclude 'META-INF/NOTICE' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + } + } + + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + + libraryVariants.all { variant -> + def buildType = variant.buildType.name.capitalize() + + def taskPrefix = "" + if (project.path != ":") { + taskPrefix = project.path + ":" + } + + // Disable the externalNativeBuild* task as it would cause build failures since the cmake build + // files is only setup for editing support. + gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType + } +} + +dependencies {} diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle index f6921c70aa..524031d93f 100644 --- a/platform/android/java/settings.gradle +++ b/platform/android/java/settings.gradle @@ -3,3 +3,4 @@ rootProject.name = "Godot" include ':app' include ':lib' +include ':nativeSrcsConfigs' diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 0a42adeaf2..8d1db395ab 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "java_godot_io_wrapper.h" -#include "core/error_list.h" +#include "core/error/error_list.h" // JNIEnv is only valid within the thread it belongs to, in a multi threading environment // we can't cache it. @@ -52,8 +52,9 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _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_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); - _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)V"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V"); _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); _get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I"); @@ -118,6 +119,19 @@ int GodotIOJavaWrapper::get_screen_dpi() { } } +void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) { + if (_screen_get_usable_rect) { + JNIEnv *env = ThreadAndroid::get_env(); + 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]; + } + env->ReleaseIntArrayElements(returnArray, arrayBody, 0); + } +} + String GodotIOJavaWrapper::get_unique_id() { if (_get_unique_id) { JNIEnv *env = ThreadAndroid::get_env(); @@ -132,11 +146,11 @@ bool GodotIOJavaWrapper::has_vk() { return (_show_keyboard != 0) && (_hide_keyboard != 0); } -void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +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 = ThreadAndroid::get_env(); jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); - env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length, p_cursor_start, p_cursor_end); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); } } diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index 1742021379..13270c794b 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -50,6 +50,7 @@ private: jmethodID _get_locale = 0; jmethodID _get_model = 0; jmethodID _get_screen_DPI = 0; + jmethodID _screen_get_usable_rect = 0; jmethodID _get_unique_id = 0; jmethodID _show_keyboard = 0; jmethodID _hide_keyboard = 0; @@ -68,9 +69,10 @@ public: String get_locale(); String get_model(); int get_screen_dpi(); + void screen_get_usable_rect(int (&p_rect_xywh)[4]); String get_unique_id(); bool has_vk(); - void show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end); + void show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end); void hide_vk(); int get_vk_height(); void set_vk_height(int p_height); diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 4610b94363..5dc773fae2 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -37,9 +37,9 @@ #include "api/java_class_wrapper.h" #include "api/jni_singleton.h" #include "audio_driver_jandroid.h" -#include "core/engine.h" +#include "core/config/engine.h" +#include "core/config/project_settings.h" #include "core/input/input.h" -#include "core/project_settings.h" #include "dir_access_jandroid.h" #include "display_server_android.h" #include "file_access_android.h" @@ -51,6 +51,7 @@ #include "string_android.h" #include "thread_jandroid.h" +#include <android/input.h> #include <unistd.h> #include <android/native_window_jni.h> @@ -237,40 +238,51 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) { +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 == 0) return; Vector<DisplayServerAndroid::TouchPos> points; - for (int i = 0; i < count; i++) { - jint p[3]; - env->GetIntArrayRegion(positions, i * 3, 3, p); + for (int i = 0; i < pointer_count; i++) { + jfloat p[3]; + env->GetFloatArrayRegion(positions, i * 3, 3, p); DisplayServerAndroid::TouchPos tp; tp.pos = Point2(p[1], p[2]); - tp.id = p[0]; + tp.id = (int)p[0]; points.push_back(tp); } - DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points); + if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) { + DisplayServerAndroid::get_singleton()->process_mouse_event(ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor); + } else { + DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points); + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) { + touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) { + touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask); +} - /* - if (os_android) - os_android->process_touch(ev,pointer,points); - */ +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) { + touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) { if (step == 0) return; DisplayServerAndroid::get_singleton()->process_hover(p_type, Point2(p_x, p_y)); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint 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) { if (step == 0) return; - DisplayServerAndroid::get_singleton()->process_double_tap(Point2(p_x, p_y)); + DisplayServerAndroid::get_singleton()->process_double_tap(p_button_mask, Point2(p_x, p_y)); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) { diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 07584518e5..b499f6dfa1 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -44,9 +44,12 @@ 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_step(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y); +void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_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); diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h index 5320715853..bda056604f 100644 --- a/platform/android/jni_utils.h +++ b/platform/android/jni_utils.h @@ -32,8 +32,8 @@ #define JNI_UTILS_H #include "string_android.h" -#include <core/engine.h> -#include <core/variant.h> +#include <core/config/engine.h> +#include <core/variant/variant.h> #include <jni.h> struct jvalret { diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differindex df445f6a9c..f44d360a25 100644 --- a/platform/android/logo.png +++ b/platform/android/logo.png diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index baf6ee952a..00733f6dbb 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -30,8 +30,8 @@ #include "os_android.h" +#include "core/config/project_settings.h" #include "core/io/file_access_buffered_fa.h" -#include "core/project_settings.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "file_access_android.h" @@ -153,6 +153,7 @@ void OS_Android::main_loop_begin() { bool OS_Android::main_loop_iterate() { if (!main_loop) return false; + DisplayServerAndroid::get_singleton()->process_events(); return Main::iteration(); } diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h index ea3c7b4f55..ecb9c0c7f5 100644 --- a/platform/android/plugin/godot_plugin_config.h +++ b/platform/android/plugin/godot_plugin_config.h @@ -31,9 +31,9 @@ #ifndef GODOT_PLUGIN_CONFIG_H #define GODOT_PLUGIN_CONFIG_H -#include "core/error_list.h" +#include "core/error/error_list.h" #include "core/io/config_file.h" -#include "core/ustring.h" +#include "core/string/ustring.h" static const char *PLUGIN_CONFIG_EXT = ".gdap"; diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index d2528bebeb..b8e5345b85 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -30,9 +30,9 @@ #include "godot_plugin_jni.h" -#include <core/engine.h> -#include <core/error_macros.h> -#include <core/project_settings.h> +#include <core/config/engine.h> +#include <core/config/project_settings.h> +#include <core/error/error_macros.h> #include <platform/android/api/jni_singleton.h> #include <platform/android/jni_utils.h> #include <platform/android/string_android.h> diff --git a/platform/android/string_android.h b/platform/android/string_android.h index 88ccd3b652..0a7dbf475d 100644 --- a/platform/android/string_android.h +++ b/platform/android/string_android.h @@ -30,7 +30,7 @@ #ifndef STRING_ANDROID_H #define STRING_ANDROID_H -#include "core/ustring.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 13aa313ebf..442e4d781b 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -30,9 +30,9 @@ #include "thread_jandroid.h" +#include "core/object/script_language.h" #include "core/os/memory.h" -#include "core/safe_refcount.h" -#include "core/script_language.h" +#include "core/templates/safe_refcount.h" static void _thread_id_key_destr_callback(void *p_value) { memdelete(static_cast<Thread::ID *>(p_value)); diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp index 5fb7a83da4..56ef99dfc7 100644 --- a/platform/android/vulkan/vulkan_context_android.cpp +++ b/platform/android/vulkan/vulkan_context_android.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "vulkan_context_android.h" + #include <vulkan/vulkan_android.h> const char *VulkanContextAndroid::_get_platform_surface_extension() const { |