diff options
Diffstat (limited to 'platform/android')
44 files changed, 687 insertions, 430 deletions
diff --git a/platform/android/README.md b/platform/android/README.md new file mode 100644 index 0000000000..343e588553 --- /dev/null +++ b/platform/android/README.md @@ -0,0 +1,14 @@ +# Android platform port + +This folder contains the Java and C++ (JNI) code for the Android platform port, +using [Gradle](https://gradle.org/) as a build system. + +## Artwork license + +[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under +[Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot) +per the Android logo usage guidelines: + +> The Android robot is reproduced or modified from work created and shared by +> Google and used according to terms described in the Creative Commons 3.0 +> Attribution License. diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index e03375e8d9..52f80b3080 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -45,7 +45,7 @@ void AndroidInputHandler::process_joy_event(AndroidInputHandler::JoypadEvent p_e Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, value); break; case JOY_EVENT_HAT: - Input::get_singleton()->joy_hat(p_event.device, (HatMask)p_event.hat); + Input::get_singleton()->joy_hat(p_event.device, p_event.hat); break; default: return; @@ -82,37 +82,37 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p Ref<InputEventKey> ev; ev.instantiate(); int val = unicode; - int keycode = android_get_keysym(p_keycode); - int phy_keycode = android_get_keysym(p_scancode); + Key keycode = android_get_keysym(p_keycode); + Key phy_keycode = android_get_keysym(p_scancode); - if (keycode == KEY_SHIFT) { + if (keycode == Key::SHIFT) { shift_mem = p_pressed; } - if (keycode == KEY_ALT) { + if (keycode == Key::ALT) { alt_mem = p_pressed; } - if (keycode == KEY_CTRL) { + if (keycode == Key::CTRL) { control_mem = p_pressed; } - if (keycode == KEY_META) { + if (keycode == Key::META) { meta_mem = p_pressed; } - ev->set_keycode((Key)keycode); - ev->set_physical_keycode((Key)phy_keycode); + ev->set_keycode(keycode); + ev->set_physical_keycode(phy_keycode); ev->set_unicode(val); ev->set_pressed(p_pressed); _set_key_modifier_state(ev); if (val == '\n') { - ev->set_keycode(KEY_ENTER); + ev->set_keycode(Key::ENTER); } else if (val == 61448) { - ev->set_keycode(KEY_BACKSPACE); - ev->set_unicode(KEY_BACKSPACE); + ev->set_keycode(Key::BACKSPACE); + ev->set_unicode((char32_t)Key::BACKSPACE); } else if (val == 61453) { - ev->set_keycode(KEY_ENTER); - ev->set_unicode(KEY_ENTER); + ev->set_keycode(Key::ENTER); + ev->set_unicode((char32_t)Key::ENTER); } else if (p_keycode == 4) { if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) { dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST, true); @@ -223,7 +223,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector ev->set_pressed(false); ev->set_position(touch[i].pos); Input::get_singleton()->parse_input_event(ev); - touch.remove(i); + touch.remove_at(i); break; } @@ -305,15 +305,15 @@ void AndroidInputHandler::process_mouse_event(int input_device, int event_action ev->set_pressed(true); buttons_state = event_buttons_mask; if (event_vertical_factor > 0) { - _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_UP, event_vertical_factor); + _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, event_vertical_factor); } else if (event_vertical_factor < 0) { - _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_DOWN, -event_vertical_factor); + _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor); } if (event_horizontal_factor > 0) { - _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_RIGHT, event_horizontal_factor); + _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor); } else if (event_horizontal_factor < 0) { - _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_LEFT, -event_horizontal_factor); + _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor); } } break; } @@ -323,7 +323,7 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co Ref<InputEventMouseButton> evd = ev->duplicate(); _set_key_modifier_state(evd); evd->set_button_index(wheel_button); - evd->set_button_mask(MouseButton(event_buttons_mask ^ (1 << (wheel_button - 1)))); + evd->set_button_mask(MouseButton(event_buttons_mask ^ mouse_button_to_mask(wheel_button))); evd->set_factor(factor); Input::get_singleton()->parse_input_event(evd); Ref<InputEventMouseButton> evdd = evd->duplicate(); @@ -339,7 +339,7 @@ void AndroidInputHandler::process_double_tap(int event_android_button_mask, Poin _set_key_modifier_state(ev); ev->set_position(p_pos); ev->set_global_position(p_pos); - ev->set_pressed(event_button_mask != 0); + ev->set_pressed(event_button_mask != MouseButton::NONE); ev->set_button_index(_button_index_from_mask(event_button_mask)); ev->set_button_mask(event_button_mask); ev->set_double_click(true); @@ -348,37 +348,37 @@ void AndroidInputHandler::process_double_tap(int event_android_button_mask, Poin MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) { switch (button_mask) { - case MOUSE_BUTTON_MASK_LEFT: - return MOUSE_BUTTON_LEFT; - case MOUSE_BUTTON_MASK_RIGHT: - return MOUSE_BUTTON_RIGHT; - case MOUSE_BUTTON_MASK_MIDDLE: - return MOUSE_BUTTON_MIDDLE; - case MOUSE_BUTTON_MASK_XBUTTON1: - return MOUSE_BUTTON_XBUTTON1; - case MOUSE_BUTTON_MASK_XBUTTON2: - return MOUSE_BUTTON_XBUTTON2; + case MouseButton::MASK_LEFT: + return MouseButton::LEFT; + case MouseButton::MASK_RIGHT: + return MouseButton::RIGHT; + case MouseButton::MASK_MIDDLE: + return MouseButton::MIDDLE; + case MouseButton::MASK_XBUTTON1: + return MouseButton::MB_XBUTTON1; + case MouseButton::MASK_XBUTTON2: + return MouseButton::MB_XBUTTON2; default: - return MOUSE_BUTTON_NONE; + return MouseButton::NONE; } } MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) { - MouseButton godot_button_mask = MOUSE_BUTTON_NONE; + MouseButton godot_button_mask = MouseButton::NONE; if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) { - godot_button_mask |= MOUSE_BUTTON_MASK_LEFT; + godot_button_mask |= MouseButton::MASK_LEFT; } if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { - godot_button_mask |= MOUSE_BUTTON_MASK_RIGHT; + godot_button_mask |= MouseButton::MASK_RIGHT; } if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) { - godot_button_mask |= MOUSE_BUTTON_MASK_MIDDLE; + godot_button_mask |= MouseButton::MASK_MIDDLE; } if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) { - godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1; + godot_button_mask |= MouseButton::MASK_XBUTTON1; } if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) { - godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2; + godot_button_mask |= MouseButton::MASK_XBUTTON2; } return godot_button_mask; diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index 2918ca300b..e0b4196f14 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -53,10 +53,10 @@ public: struct JoypadEvent { int device = 0; int type = 0; - int index = 0; + int index = 0; // Can be either JoyAxis or JoyButton. bool pressed = false; float value = 0; - int hat = 0; + HatMask hat = HatMask::CENTER; }; private: @@ -65,7 +65,7 @@ private: bool control_mem = false; bool meta_mem = false; - MouseButton buttons_state = MOUSE_BUTTON_NONE; + MouseButton buttons_state = MouseButton::NONE; Vector<TouchPos> touch; Point2 hover_prev_pos; // needed to calculate the relative position on hover events diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp index 5aa546c17b..0cea0589ce 100644 --- a/platform/android/android_keys_utils.cpp +++ b/platform/android/android_keys_utils.cpp @@ -30,12 +30,12 @@ #include "android_keys_utils.h" -unsigned int android_get_keysym(unsigned int p_code) { - for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) { +Key android_get_keysym(unsigned int p_code) { + for (int i = 0; _ak_to_keycode[i].keysym != Key::UNKNOWN; i++) { if (_ak_to_keycode[i].keycode == p_code) { return _ak_to_keycode[i].keysym; } } - return KEY_UNKNOWN; + return Key::UNKNOWN; } diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index 6d25a366a4..bac9bab6db 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -35,128 +35,128 @@ #include <core/os/keyboard.h> struct _WinTranslatePair { - unsigned int keysym = 0; + Key keysym = Key::NONE; unsigned int keycode = 0; }; static _WinTranslatePair _ak_to_keycode[] = { - { KEY_TAB, AKEYCODE_TAB }, - { KEY_ENTER, AKEYCODE_ENTER }, - { KEY_SHIFT, AKEYCODE_SHIFT_LEFT }, - { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT }, - { KEY_ALT, AKEYCODE_ALT_LEFT }, - { KEY_ALT, AKEYCODE_ALT_RIGHT }, - { KEY_MENU, AKEYCODE_MENU }, - { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE }, - { KEY_ESCAPE, AKEYCODE_BACK }, - { KEY_SPACE, AKEYCODE_SPACE }, - { KEY_PAGEUP, AKEYCODE_PAGE_UP }, - { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN }, - { KEY_HOME, AKEYCODE_HOME }, //(0x24) - { KEY_LEFT, AKEYCODE_DPAD_LEFT }, - { KEY_UP, AKEYCODE_DPAD_UP }, - { KEY_RIGHT, AKEYCODE_DPAD_RIGHT }, - { KEY_DOWN, AKEYCODE_DPAD_DOWN }, - { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER }, - { KEY_BACKSPACE, AKEYCODE_DEL }, - { KEY_0, AKEYCODE_0 }, ////0 key - { KEY_1, AKEYCODE_1 }, ////1 key - { KEY_2, AKEYCODE_2 }, ////2 key - { KEY_3, AKEYCODE_3 }, ////3 key - { KEY_4, AKEYCODE_4 }, ////4 key - { KEY_5, AKEYCODE_5 }, ////5 key - { KEY_6, AKEYCODE_6 }, ////6 key - { KEY_7, AKEYCODE_7 }, ////7 key - { KEY_8, AKEYCODE_8 }, ////8 key - { KEY_9, AKEYCODE_9 }, ////9 key - { KEY_A, AKEYCODE_A }, ////A key - { KEY_B, AKEYCODE_B }, ////B key - { KEY_C, AKEYCODE_C }, ////C key - { KEY_D, AKEYCODE_D }, ////D key - { KEY_E, AKEYCODE_E }, ////E key - { KEY_F, AKEYCODE_F }, ////F key - { KEY_G, AKEYCODE_G }, ////G key - { KEY_H, AKEYCODE_H }, ////H key - { KEY_I, AKEYCODE_I }, ////I key - { KEY_J, AKEYCODE_J }, ////J key - { KEY_K, AKEYCODE_K }, ////K key - { KEY_L, AKEYCODE_L }, ////L key - { KEY_M, AKEYCODE_M }, ////M key - { KEY_N, AKEYCODE_N }, ////N key - { KEY_O, AKEYCODE_O }, ////O key - { KEY_P, AKEYCODE_P }, ////P key - { KEY_Q, AKEYCODE_Q }, ////Q key - { KEY_R, AKEYCODE_R }, ////R key - { KEY_S, AKEYCODE_S }, ////S key - { KEY_T, AKEYCODE_T }, ////T key - { KEY_U, AKEYCODE_U }, ////U key - { KEY_V, AKEYCODE_V }, ////V key - { KEY_W, AKEYCODE_W }, ////W key - { KEY_X, AKEYCODE_X }, ////X key - { KEY_Y, AKEYCODE_Y }, ////Y key - { KEY_Z, AKEYCODE_Z }, ////Z key - { KEY_HOMEPAGE, AKEYCODE_EXPLORER }, - { KEY_LAUNCH0, AKEYCODE_BUTTON_A }, - { KEY_LAUNCH1, AKEYCODE_BUTTON_B }, - { KEY_LAUNCH2, AKEYCODE_BUTTON_C }, - { KEY_LAUNCH3, AKEYCODE_BUTTON_X }, - { KEY_LAUNCH4, AKEYCODE_BUTTON_Y }, - { KEY_LAUNCH5, AKEYCODE_BUTTON_Z }, - { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 }, - { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 }, - { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 }, - { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 }, - { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL }, - { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR }, - { KEY_LAUNCHC, AKEYCODE_BUTTON_START }, - { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT }, - { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE }, - { KEY_VOLUMEMUTE, AKEYCODE_MUTE }, - { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN }, - { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP }, - { KEY_BACK, AKEYCODE_MEDIA_REWIND }, - { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD }, - { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT }, - { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS }, - { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP }, - { KEY_PLUS, AKEYCODE_PLUS }, - { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key - { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key - { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key - { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key - { KEY_BACKSLASH, AKEYCODE_BACKSLASH }, - { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, - { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, - { KEY_CTRL, AKEYCODE_CTRL_LEFT }, - { KEY_CTRL, AKEYCODE_CTRL_RIGHT }, - { KEY_UNKNOWN, 0 } + { Key::TAB, AKEYCODE_TAB }, + { Key::ENTER, AKEYCODE_ENTER }, + { Key::SHIFT, AKEYCODE_SHIFT_LEFT }, + { Key::SHIFT, AKEYCODE_SHIFT_RIGHT }, + { Key::ALT, AKEYCODE_ALT_LEFT }, + { Key::ALT, AKEYCODE_ALT_RIGHT }, + { Key::MENU, AKEYCODE_MENU }, + { Key::PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE }, + { Key::ESCAPE, AKEYCODE_BACK }, + { Key::SPACE, AKEYCODE_SPACE }, + { Key::PAGEUP, AKEYCODE_PAGE_UP }, + { Key::PAGEDOWN, AKEYCODE_PAGE_DOWN }, + { Key::HOME, AKEYCODE_HOME }, //(0x24) + { Key::LEFT, AKEYCODE_DPAD_LEFT }, + { Key::UP, AKEYCODE_DPAD_UP }, + { Key::RIGHT, AKEYCODE_DPAD_RIGHT }, + { Key::DOWN, AKEYCODE_DPAD_DOWN }, + { Key::PERIODCENTERED, AKEYCODE_DPAD_CENTER }, + { Key::BACKSPACE, AKEYCODE_DEL }, + { Key::KEY_0, AKEYCODE_0 }, + { Key::KEY_1, AKEYCODE_1 }, + { Key::KEY_2, AKEYCODE_2 }, + { Key::KEY_3, AKEYCODE_3 }, + { Key::KEY_4, AKEYCODE_4 }, + { Key::KEY_5, AKEYCODE_5 }, + { Key::KEY_6, AKEYCODE_6 }, + { Key::KEY_7, AKEYCODE_7 }, + { Key::KEY_8, AKEYCODE_8 }, + { Key::KEY_9, AKEYCODE_9 }, + { Key::A, AKEYCODE_A }, + { Key::B, AKEYCODE_B }, + { Key::C, AKEYCODE_C }, + { Key::D, AKEYCODE_D }, + { Key::E, AKEYCODE_E }, + { Key::F, AKEYCODE_F }, + { Key::G, AKEYCODE_G }, + { Key::H, AKEYCODE_H }, + { Key::I, AKEYCODE_I }, + { Key::J, AKEYCODE_J }, + { Key::K, AKEYCODE_K }, + { Key::L, AKEYCODE_L }, + { Key::M, AKEYCODE_M }, + { Key::N, AKEYCODE_N }, + { Key::O, AKEYCODE_O }, + { Key::P, AKEYCODE_P }, + { Key::Q, AKEYCODE_Q }, + { Key::R, AKEYCODE_R }, + { Key::S, AKEYCODE_S }, + { Key::T, AKEYCODE_T }, + { Key::U, AKEYCODE_U }, + { Key::V, AKEYCODE_V }, + { Key::W, AKEYCODE_W }, + { Key::X, AKEYCODE_X }, + { Key::Y, AKEYCODE_Y }, + { Key::Z, AKEYCODE_Z }, + { Key::HOMEPAGE, AKEYCODE_EXPLORER }, + { Key::LAUNCH0, AKEYCODE_BUTTON_A }, + { Key::LAUNCH1, AKEYCODE_BUTTON_B }, + { Key::LAUNCH2, AKEYCODE_BUTTON_C }, + { Key::LAUNCH3, AKEYCODE_BUTTON_X }, + { Key::LAUNCH4, AKEYCODE_BUTTON_Y }, + { Key::LAUNCH5, AKEYCODE_BUTTON_Z }, + { Key::LAUNCH6, AKEYCODE_BUTTON_L1 }, + { Key::LAUNCH7, AKEYCODE_BUTTON_R1 }, + { Key::LAUNCH8, AKEYCODE_BUTTON_L2 }, + { Key::LAUNCH9, AKEYCODE_BUTTON_R2 }, + { Key::LAUNCHA, AKEYCODE_BUTTON_THUMBL }, + { Key::LAUNCHB, AKEYCODE_BUTTON_THUMBR }, + { Key::LAUNCHC, AKEYCODE_BUTTON_START }, + { Key::LAUNCHD, AKEYCODE_BUTTON_SELECT }, + { Key::LAUNCHE, AKEYCODE_BUTTON_MODE }, + { Key::VOLUMEMUTE, AKEYCODE_MUTE }, + { Key::VOLUMEDOWN, AKEYCODE_VOLUME_DOWN }, + { Key::VOLUMEUP, AKEYCODE_VOLUME_UP }, + { Key::BACK, AKEYCODE_MEDIA_REWIND }, + { Key::FORWARD, AKEYCODE_MEDIA_FAST_FORWARD }, + { Key::MEDIANEXT, AKEYCODE_MEDIA_NEXT }, + { Key::MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS }, + { Key::MEDIASTOP, AKEYCODE_MEDIA_STOP }, + { Key::PLUS, AKEYCODE_PLUS }, + { Key::EQUAL, AKEYCODE_EQUALS }, // the '+' key + { Key::COMMA, AKEYCODE_COMMA }, // the ',' key + { Key::MINUS, AKEYCODE_MINUS }, // the '-' key + { Key::SLASH, AKEYCODE_SLASH }, // the '/?' key + { Key::BACKSLASH, AKEYCODE_BACKSLASH }, + { Key::BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, + { Key::BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, + { Key::CTRL, AKEYCODE_CTRL_LEFT }, + { Key::CTRL, AKEYCODE_CTRL_RIGHT }, + { Key::UNKNOWN, 0 } }; /* TODO: map these android key: - AKEYCODE_SOFT_LEFT = 1, - AKEYCODE_SOFT_RIGHT = 2, - AKEYCODE_CALL = 5, - AKEYCODE_ENDCALL = 6, - AKEYCODE_STAR = 17, - AKEYCODE_POUND = 18, - AKEYCODE_POWER = 26, - AKEYCODE_CAMERA = 27, - AKEYCODE_CLEAR = 28, - AKEYCODE_SYM = 63, - AKEYCODE_ENVELOPE = 65, - AKEYCODE_GRAVE = 68, - AKEYCODE_SEMICOLON = 74, - AKEYCODE_APOSTROPHE = 75, - AKEYCODE_AT = 77, - AKEYCODE_NUM = 78, - AKEYCODE_HEADSETHOOK = 79, - AKEYCODE_FOCUS = 80, // *Camera* focus - AKEYCODE_NOTIFICATION = 83, - AKEYCODE_SEARCH = 84, - AKEYCODE_PICTSYMBOLS = 94, - AKEYCODE_SWITCH_CHARSET = 95, + AKEYCODE_SOFT_LEFT = 1, + AKEYCODE_SOFT_RIGHT = 2, + AKEYCODE_CALL = 5, + AKEYCODE_ENDCALL = 6, + AKEYCODE_STAR = 17, + AKEYCODE_POUND = 18, + AKEYCODE_POWER = 26, + AKEYCODE_CAMERA = 27, + AKEYCODE_CLEAR = 28, + AKEYCODE_SYM = 63, + AKEYCODE_ENVELOPE = 65, + AKEYCODE_GRAVE = 68, + AKEYCODE_SEMICOLON = 74, + AKEYCODE_APOSTROPHE = 75, + AKEYCODE_AT = 77, + AKEYCODE_NUM = 78, + AKEYCODE_HEADSETHOOK = 79, + AKEYCODE_FOCUS = 80, // *Camera* focus + AKEYCODE_NOTIFICATION = 83, + AKEYCODE_SEARCH = 84, + AKEYCODE_PICTSYMBOLS = 94, + AKEYCODE_SWITCH_CHARSET = 95, */ -unsigned int android_get_keysym(unsigned int p_code); +Key android_get_keysym(unsigned int p_code); #endif // ANDROID_KEYS_UTILS_H diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h index e3efaddba2..fcc2513f3f 100644 --- a/platform/android/audio_driver_opensl.h +++ b/platform/android/audio_driver_opensl.h @@ -59,7 +59,6 @@ class AudioDriverOpenSL : public AudioDriver { SLObjectItf sl; SLEngineItf EngineItf; SLObjectItf OutputMix; - SLVolumeItf volumeItf; SLObjectItf player; SLObjectItf recorder; SLAndroidSimpleBufferQueueItf bufferQueueItf; @@ -68,7 +67,6 @@ class AudioDriverOpenSL : public AudioDriver { SLDataFormat_PCM pcm; SLDataSink audioSink; SLDataLocator_OutputMix locator_outputmix; - SLBufferQueueState state; static AudioDriverOpenSL *s_ad; @@ -89,8 +87,6 @@ class AudioDriverOpenSL : public AudioDriver { virtual Error capture_init_device(); public: - void set_singleton(); - virtual const char *get_name() const; virtual Error init(); diff --git a/platform/android/detect.py b/platform/android/detect.py index 61ccad9ac3..3319d5890c 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -27,8 +27,7 @@ def get_opts(): ("ANDROID_NDK_ROOT", "Path to the Android NDK", get_android_ndk_root()), ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_android_sdk_root()), ("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"), - EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")), - BoolVariable("android_neon", "Enable NEON support (armv7 only)", True), + EnumVariable("android_arch", "Target architecture", "arm64v8", ("armv7", "arm64v8", "x86", "x86_64")), ] @@ -143,10 +142,7 @@ def configure(env): if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]: env["android_arch"] = "armv7" - neon_text = "" - if env["android_arch"] == "armv7" and env["android_neon"]: - neon_text = " (with NEON)" - print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")" + neon_text) + print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")") can_vectorize = True if env["android_arch"] == "x86": @@ -156,7 +152,7 @@ def configure(env): abi_subpath = "i686-linux-android" arch_subpath = "x86" env["x86_libtheora_opt_gcc"] = True - if env["android_arch"] == "x86_64": + elif 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" @@ -174,10 +170,7 @@ def configure(env): target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" arch_subpath = "armeabi-v7a" - if env["android_neon"]: - env.extra_suffix = ".armv7.neon" + env.extra_suffix - else: - env.extra_suffix = ".armv7" + env.extra_suffix + env.extra_suffix = ".armv7" + env.extra_suffix elif env["android_arch"] == "arm64v8": if get_platform(env["ndk_platform"]) < 21: print( @@ -204,12 +197,10 @@ def configure(env): env.Append(CPPDEFINES=["NDEBUG"]) if can_vectorize: env.Append(CCFLAGS=["-ftree-vectorize"]) - if env["target"] == "release_debug": - env.Append(CPPDEFINES=["DEBUG_ENABLED"]) elif env["target"] == "debug": env.Append(LINKFLAGS=["-O0"]) env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"]) - env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED"]) + env.Append(CPPDEFINES=["_DEBUG"]) env.Append(CPPFLAGS=["-UNDEBUG"]) # Compiler configuration @@ -288,7 +279,6 @@ def configure(env): if get_platform(env["ndk_platform"]) >= 24: env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)]) - env["neon_enabled"] = False if env["android_arch"] == "x86": target_opts = ["-target", "i686-none-linux-android"] # The NDK adds this if targeting API < 21, so we can drop it when Godot targets it at least @@ -301,12 +291,9 @@ def configure(env): target_opts = ["-target", "armv7-none-linux-androideabi"] env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split()) env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"]) - if env["android_neon"]: - env["neon_enabled"] = True - env.Append(CCFLAGS=["-mfpu=neon"]) - env.Append(CPPDEFINES=["__ARM_NEON__"]) - else: - env.Append(CCFLAGS=["-mfpu=vfpv3-d16"]) + # Enable ARM NEON instructions to compile more optimized code. + env.Append(CCFLAGS=["-mfpu=neon"]) + env.Append(CPPDEFINES=["__ARM_NEON__"]) elif env["android_arch"] == "arm64v8": target_opts = ["-target", "aarch64-none-linux-android"] diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 0bae090702..d2726b8652 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -102,7 +102,7 @@ String DirAccessJAndroid::get_drive(int p_drive) { Error DirAccessJAndroid::change_dir(String p_dir) { JNIEnv *env = get_jni_env(); - if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == "")) + if (p_dir.is_empty() || p_dir == "." || (p_dir == ".." && current_dir.is_empty())) return OK; String new_dir; @@ -114,7 +114,7 @@ Error DirAccessJAndroid::change_dir(String p_dir) { new_dir = p_dir.substr(1, p_dir.length()); else if (p_dir.begins_with("res://")) new_dir = p_dir.substr(6, p_dir.length()); - else if (current_dir == "") + else if (current_dir.is_empty()) new_dir = p_dir; else new_dir = current_dir.plus_file(p_dir); @@ -141,7 +141,7 @@ String DirAccessJAndroid::get_current_dir(bool p_include_drive) { bool DirAccessJAndroid::file_exists(String p_file) { String sd; - if (current_dir == "") + if (current_dir.is_empty()) sd = p_file; else sd = current_dir.plus_file(p_file); @@ -158,10 +158,10 @@ bool DirAccessJAndroid::dir_exists(String p_dir) { String sd; - if (current_dir == "") + if (current_dir.is_empty()) sd = p_dir; else { - if (p_dir.is_rel_path()) + if (p_dir.is_relative_path()) sd = current_dir.plus_file(p_dir); else sd = fix_path(p_dir); diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 720752d28f..505e7ac0eb 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -119,7 +119,9 @@ DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(in GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); ERR_FAIL_COND_V(!godot_io_java, SCREEN_LANDSCAPE); - return (ScreenOrientation)godot_io_java->get_screen_orientation(); + const int orientation = godot_io_java->get_screen_orientation(); + ERR_FAIL_INDEX_V_MSG(orientation, 7, SCREEN_LANDSCAPE, "Unrecognized screen orientation"); + return (ScreenOrientation)orientation; } int DisplayServerAndroid::get_screen_count() const { @@ -344,8 +346,8 @@ void DisplayServerAndroid::process_events() { Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { Vector<String> drivers; -#ifdef OPENGL_ENABLED - drivers.push_back("opengl"); +#ifdef GLES3_ENABLED + drivers.push_back("opengl3"); #endif #ifdef VULKAN_ENABLED drivers.push_back("vulkan"); @@ -407,13 +409,13 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on"); -#if defined(OPENGL_ENABLED) - if (rendering_driver == "opengl") { +#if defined(GLES3_ENABLED) + if (rendering_driver == "opengl3") { bool gl_initialization_error = false; - if (RasterizerGLES2::is_viable() == OK) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); + if (RasterizerGLES3::is_viable() == OK) { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); } else { gl_initialization_error = true; } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index bdf12b6c50..70b36f2350 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -221,6 +221,13 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun static const int EXPORT_FORMAT_APK = 0; static const int EXPORT_FORMAT_AAB = 1; +static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets"; +static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets"; + +static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk' +static const int DEFAULT_TARGET_SDK_VERSION = 30; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk' +const String SDK_VERSION_RANGE = vformat("%s,%s,1", DEFAULT_MIN_SDK_VERSION, DEFAULT_TARGET_SDK_VERSION); + void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; @@ -300,7 +307,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { } } - if (d.description == "") { + if (d.description.is_empty()) { //in the oven, request! args.clear(); args.push_back("-s"); @@ -349,7 +356,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { } d.name = vendor + " " + device; - if (device == String()) { + if (device.is_empty()) { continue; } } @@ -387,13 +394,13 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { String EditorExportPlatformAndroid::get_project_name(const String &p_name) const { String aname; - if (p_name != "") { + if (!p_name.is_empty()) { aname = p_name; } else { aname = ProjectSettings::get_singleton()->get("application/config/name"); } - if (aname == "") { + if (aname.is_empty()) { aname = VERSION_NAME; } @@ -417,7 +424,7 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co first = false; } } - if (name == "") { + if (name.is_empty()) { name = "noname"; } @@ -426,6 +433,10 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co return pname; } +String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const { + return p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY; +} + bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const { String pname = p_package; @@ -491,11 +502,11 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected attime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected attime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ // -- Unconditional uncompress to mimic AAPT plus some other @@ -570,7 +581,7 @@ Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path da->list_dir_begin(); while (true) { String file = da->get_next(); - if (file == "") { + if (file.is_empty()) { break; } @@ -744,9 +755,9 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> } int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1 /* XRMode.OVR */) { + if (xr_mode_index == XR_MODE_OPENXR) { int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { + if (hand_tracking_index > XR_HAND_TRACKING_NONE) { if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); } @@ -825,10 +836,13 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); int xr_mode_index = p_preset->get("xr_features/xr_mode"); + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); + int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); bool backup_allowed = p_preset->get("user_data_backup/allow"); bool classify_as_game = p_preset->get("package/classify_as_game"); bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall"); + bool exclude_from_recents = p_preset->get("package/exclude_from_recents"); Vector<String> perms; // Write permissions into the perms variable. @@ -844,16 +858,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p int iofs = ofs + 8; string_count = decode_uint32(&p_manifest[iofs]); - //styles_count = decode_uint32(&p_manifest[iofs + 4]); + // iofs + 4 is `styles_count`. string_flags = decode_uint32(&p_manifest[iofs + 8]); string_data_offset = decode_uint32(&p_manifest[iofs + 12]); - //styles_offset = decode_uint32(&p_manifest[iofs + 16]); - /* - printf("string count: %i\n",string_count); - printf("flags: %i\n",string_flags); - printf("sdata ofs: %i\n",string_data_offset); - printf("styles ofs: %i\n",styles_offset); - */ + // iofs + 16 is `styles_offset`. + uint32_t st_offset = iofs + 20; string_table.resize(string_count); uint32_t string_end = 0; @@ -947,6 +956,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); } + if (tname == "activity" && attrname == "excludeFromRecents") { + encode_uint32(exclude_from_recents, &p_manifest.write[iofs + 16]); + } + if (tname == "supports-screens") { if (attrname == "smallScreens") { encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); @@ -962,6 +975,32 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p } } + if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") { + // Update the meta-data 'android:name' attribute based on the selected XR mode. + if (xr_mode_index == XR_MODE_OPENXR) { + string_table.write[attr_value] = "com.samsung.android.vr.application.mode"; + } + } + + if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") { + // Update the meta-data 'android:value' attribute based on the selected XR mode. + if (xr_mode_index == XR_MODE_OPENXR) { + string_table.write[attr_value] = "vr_only"; + } + } + + if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_metadata_name") { + if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) { + string_table.write[attr_value] = "com.oculus.handtracking.frequency"; + } + } + + if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_metadata_value") { + if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) { + string_table.write[attr_value] = (hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH"); + } + } + iofs += 20; } @@ -976,14 +1015,26 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<bool> feature_required_list; Vector<int> feature_versions; - if (xr_mode_index == 1 /* XRMode.OVR */) { + if (xr_mode_index == XR_MODE_OPENXR) { + // Set degrees of freedom + feature_names.push_back("android.hardware.vr.headtracking"); + feature_required_list.push_back(true); + feature_versions.push_back(1); + // Check for hand tracking - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { + if (hand_tracking_index > XR_HAND_TRACKING_NONE) { feature_names.push_back("oculus.software.handtracking"); - feature_required_list.push_back(hand_tracking_index == 2); + feature_required_list.push_back(hand_tracking_index == XR_HAND_TRACKING_REQUIRED); feature_versions.push_back(-1); // no version attribute should be added. } + + // Check for passthrough + int passthrough_mode = p_preset->get("xr_features/passthrough"); + if (passthrough_mode > XR_PASSTHROUGH_NONE) { + feature_names.push_back("com.oculus.feature.PASSTHROUGH"); + feature_required_list.push_back(passthrough_mode == XR_PASSTHROUGH_REQUIRED); + feature_versions.push_back(-1); + } } if (feature_names.size() > 0) { @@ -1605,11 +1656,11 @@ Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExp void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - if (driver == "GLES2") { + if (driver == "opengl3") { r_features->push_back("etc"); } // FIXME: Review what texture formats are used for Vulkan. - if (driver == "Vulkan") { + if (driver == "vulkan") { r_features->push_back("etc2"); } @@ -1632,10 +1683,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio } plugins_changed.clear(); - Vector<String> abis = get_abis(); + const Vector<String> abis = get_abis(); for (int i = 0; i < abis.size(); ++i) { - String abi = abis[i]; - bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a"); + const String abi = abis[i]; + // All Android devices supporting Vulkan run 64-bit Android, + // so there is usually no point in exporting for 32-bit Android. + const bool is_default = abi == "arm64-v8a"; r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); } @@ -1648,21 +1701,26 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/min_sdk", PROPERTY_HINT_RANGE, SDK_VERSION_RANGE), DEFAULT_MIN_SDK_VERSION)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/target_sdk", PROPERTY_HINT_RANGE, SDK_VERSION_RANGE), DEFAULT_TARGET_SDK_VERSION)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,OpenXR"), XR_MODE_REGULAR)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_HAND_TRACKING_NONE)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking_frequency", PROPERTY_HINT_ENUM, "Low,High"), XR_HAND_TRACKING_FREQUENCY_LOW)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/passthrough", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_PASSTHROUGH_NONE)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); @@ -1971,10 +2029,11 @@ String EditorExportPlatformAndroid::get_apksigner_path() { bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { String err; bool valid = false; + const bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); // Look for export templates (first official, and if defined custom templates). - if (!bool(p_preset->get("custom_template/use_custom_build"))) { + if (!custom_build_enabled) { String template_err; bool dvalid = false; bool rvalid = false; @@ -2049,7 +2108,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr } String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - if (sdk_path == "") { + if (sdk_path.is_empty()) { err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; valid = false; } else { @@ -2096,7 +2155,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr if (apk_expansion) { String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - if (apk_expansion_pkey == "") { + if (apk_expansion_pkey.is_empty()) { valid = false; err += TTR("Invalid public key for APK expansion.") + "\n"; @@ -2112,14 +2171,13 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr } String etc_error = test_etc2(); - if (etc_error != String()) { + if (!etc_error.is_empty()) { valid = false; err += etc_error; } // Ensure that `Use Custom Build` is enabled if a plugin is selected. String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset)); - bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { valid = false; err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); @@ -2129,21 +2187,50 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr // Validate the Xr features are properly populated int xr_mode_index = p_preset->get("xr_features/xr_mode"); int hand_tracking = p_preset->get("xr_features/hand_tracking"); - if (xr_mode_index != /* XRMode.OVR*/ 1) { - if (hand_tracking > 0) { + int passthrough_mode = p_preset->get("xr_features/passthrough"); + if (xr_mode_index != XR_MODE_OPENXR) { + if (hand_tracking > XR_HAND_TRACKING_NONE) { + valid = false; + err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"OpenXR\"."); + err += "\n"; + } + + if (passthrough_mode > XR_PASSTHROUGH_NONE) { valid = false; - err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += TTR("\"Passthrough\" is only valid when \"Xr Mode\" is \"OpenXR\"."); err += "\n"; } } if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB && - !bool(p_preset->get("custom_template/use_custom_build"))) { + !custom_build_enabled) { valid = false; err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); err += "\n"; } + // Check the min sdk version + int min_sdk_version = p_preset->get("version/min_sdk"); + if (min_sdk_version != DEFAULT_MIN_SDK_VERSION && !custom_build_enabled) { + valid = false; + err += TTR("Changing the \"Min Sdk\" is only valid when \"Use Custom Build\" is enabled."); + err += "\n"; + } + + // Check the target sdk version + int target_sdk_version = p_preset->get("version/target_sdk"); + if (target_sdk_version != DEFAULT_TARGET_SDK_VERSION && !custom_build_enabled) { + valid = false; + err += TTR("Changing the \"Target Sdk\" is only valid when \"Use Custom Build\" is enabled."); + err += "\n"; + } + + if (target_sdk_version < min_sdk_version) { + valid = false; + err += TTR("\"Target Sdk\" version must be greater or equal to \"Min Sdk\" version."); + err += "\n"; + } + r_error = err; return valid; } @@ -2174,7 +2261,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP Vector<String> command_line_strings = cmdline.strip_edges().split(" "); for (int i = 0; i < command_line_strings.size(); i++) { if (command_line_strings[i].strip_edges().length() == 0) { - command_line_strings.remove(i); + command_line_strings.remove_at(i); i--; } } @@ -2194,17 +2281,12 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP } int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1) { - command_line_strings.push_back("--xr_mode_ovr"); + if (xr_mode_index == XR_MODE_OPENXR) { + command_line_strings.push_back("--xr_mode_openxr"); } else { // XRMode.REGULAR is the default. command_line_strings.push_back("--xr_mode_regular"); } - bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer"); - if (use_32_bit_framebuffer) { - command_line_strings.push_back("--use_depth_32"); - } - bool immersive = p_preset->get("screen/immersive_mode"); if (immersive) { command_line_strings.push_back("--use_immersive"); @@ -2330,11 +2412,21 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre void EditorExportPlatformAndroid::_clear_assets_directory() { DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (da_res->dir_exists("res://android/build/assets")) { - print_verbose("Clearing assets directory.."); - DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); + + // Clear the APK assets directory + if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) { + print_verbose("Clearing APK assets directory.."); + DirAccessRef da_assets = DirAccess::open(APK_ASSETS_DIRECTORY); + da_assets->erase_contents_recursive(); + da_res->remove(APK_ASSETS_DIRECTORY); + } + + // Clear the AAB assets directory + if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) { + print_verbose("Clearing AAB assets directory.."); + DirAccessRef da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY); da_assets->erase_contents_recursive(); - da_res->remove("res://android/build/assets"); + da_res->remove(AAB_ASSETS_DIRECTORY); } } @@ -2454,6 +2546,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP return ERR_UNCONFIGURED; } } + const String assets_directory = get_assets_directory(p_preset, export_format); String sdk_path = EDITOR_GET("export/android/android_sdk_path"); ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); print_verbose("Android sdk path: " + sdk_path); @@ -2475,6 +2568,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP if (!apk_expansion) { print_verbose("Exporting project files.."); CustomExportData user_data; + user_data.assets_directory = assets_directory; user_data.debug = p_debug; err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); if (err != OK) { @@ -2496,7 +2590,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } } print_verbose("Storing command line flags.."); - store_file_at_path("res://android/build/assets/_cl_", command_line_flags); + store_file_at_path(assets_directory + "/_cl_", command_line_flags); print_verbose("Updating ANDROID_HOME environment to " + sdk_path); OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required @@ -2514,6 +2608,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String package_name = get_package_name(p_preset->get("package/unique_name")); String version_code = itos(p_preset->get("version/code")); String version_name = p_preset->get("version/name"); + String min_sdk_version = itos(p_preset->get("version/min_sdk")); + String target_sdk_version = itos(p_preset->get("version/target_sdk")); String enabled_abi_string = String("|").join(enabled_abis); String sign_flag = should_sign ? "true" : "false"; String zipalign_flag = "true"; @@ -2543,6 +2639,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. + cmdline.push_back("-Pexport_version_min_sdk=" + min_sdk_version); // argument to specify the min sdk. + cmdline.push_back("-Pexport_version_target_sdk=" + target_sdk_version); // argument to specify the target sdk. cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. @@ -2609,7 +2707,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String export_filename = p_path.get_file(); String export_path = p_path.get_base_dir(); - if (export_path.is_rel_path()) { + if (export_path.is_relative_path()) { export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); } export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); @@ -2635,13 +2733,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP src_apk = p_preset->get("custom_template/release"); } src_apk = src_apk.strip_edges(); - if (src_apk == "") { + if (src_apk.is_empty()) { if (p_debug) { src_apk = find_export_template("android_debug.apk"); } else { src_apk = find_export_template("android_release.apk"); } - if (src_apk == "") { + if (src_apk.is_empty()) { EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk)); return ERR_FILE_NOT_FOUND; } @@ -2937,7 +3035,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP void EditorExportPlatformAndroid::get_platform_features(List<String> *r_features) { r_features->push_back("mobile"); - r_features->push_back("Android"); + r_features->push_back("android"); } void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index b061ee4e04..e0ffaa718b 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -87,11 +87,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { EditorProgress *ep = nullptr; }; - struct CustomExportData { - bool debug; - Vector<String> libs; - }; - Vector<PluginConfigAndroid> plugins; String last_plugin_names; uint64_t last_custom_build_time = 0; @@ -109,6 +104,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String get_package_name(const String &p_package) const; + String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const; + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data); diff --git a/platform/android/export/godot_plugin_config.cpp b/platform/android/export/godot_plugin_config.cpp index ba7b8ce6c7..205cba3350 100644 --- a/platform/android/export/godot_plugin_config.cpp +++ b/platform/android/export/godot_plugin_config.cpp @@ -78,14 +78,13 @@ Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plu bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) { bool valid_name = !plugin_config.name.is_empty(); bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL || - plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE; + plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE; bool valid_binary = false; if (valid_binary_type) { valid_binary = !plugin_config.binary.is_empty() && - (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE || - - FileAccess::exists(plugin_config.binary)); + (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE || + FileAccess::exists(plugin_config.binary)); } bool valid_local_dependencies = true; diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index b9e28a7937..27c84baa44 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -121,17 +121,33 @@ Error store_string_at_path(const String &p_path, const String &p_data) { // It's functionality mirrors that of the method save_apk_file. // This method will be called ONLY when custom build is enabled. Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); + CustomExportData *export_data = (CustomExportData *)p_userdata; + String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/"); print_verbose("Saving project files from " + p_path + " into " + dst_path); Error err = store_file_at_path(dst_path, p_data); return err; } +String _android_xml_escape(const String &p_string) { + // Android XML requires strings to be both valid XML (`xml_escape()`) but also + // to escape characters which are valid XML but have special meaning in Android XML. + // https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling + // Note: Didn't handle U+XXXX unicode chars, could be done if needed. + return p_string + .replace("@", "\\@") + .replace("?", "\\?") + .replace("'", "\\'") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\t", "\\t") + .xml_escape(false); +} + // Creates strings.xml files inside the gradle project for different locales. Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) { print_verbose("Creating strings resources for supported locales for project " + project_name); // Stores the string into the default values directory. - String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); + String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(project_name)); store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string); // Searches the Gradle project res/ directory to find all supported locales @@ -145,7 +161,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset da->list_dir_begin(); while (true) { String file = da->get_next(); - if (file == "") { + if (file.is_empty()) { break; } if (!file.begins_with("values-")) { @@ -157,7 +173,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset 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)); + String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name)); print_verbose("Storing project name for locale " + locale + " under " + locale_directory); store_string_at_path(locale_directory, processed_xml_string); } else { @@ -175,7 +191,7 @@ String bool_to_string(bool v) { String _get_gles_tag() { bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" && - !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); + !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; } @@ -195,14 +211,24 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { String manifest_xr_features; - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); + bool uses_xr = xr_mode_index == XR_MODE_OPENXR; if (uses_xr) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n"; + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index == 1) { + if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; - } else if (hand_tracking_index == 2) { + } else if (hand_tracking_index == XR_HAND_TRACKING_REQUIRED) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; } + + int passthrough_mode = p_preset->get("xr_features/passthrough"); + if (passthrough_mode == XR_PASSTHROUGH_OPTIONAL) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"false\" />\n"; + } else if (passthrough_mode == XR_PASSTHROUGH_REQUIRED) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n"; + } } return manifest_xr_features; } @@ -221,14 +247,19 @@ String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) { } String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); + bool uses_xr = xr_mode_index == XR_MODE_OPENXR; String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); String manifest_activity_text = vformat( " <activity android:name=\"com.godot.game.GodotApp\" " - "tools:replace=\"android:screenOrientation\" " + "tools:replace=\"android:screenOrientation,android:excludeFromRecents\" " + "android:excludeFromRecents=\"%s\" " "android:screenOrientation=\"%s\">\n", + bool_to_string(p_preset->get("package/exclude_from_recents")), orientation); - if (!uses_xr) { + if (uses_xr) { + manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n"; + } else { manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; } manifest_activity_text += " </activity>\n"; @@ -236,6 +267,8 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { } String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission) { + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); + bool uses_xr = xr_mode_index == XR_MODE_OPENXR; String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" @@ -244,12 +277,26 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_ " android:hasFragileUserData=\"%s\"\n" " android:requestLegacyExternalStorage=\"%s\"\n" " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" - " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", + " tools:ignore=\"GoogleAppIndexingWarning\">\n\n" + " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n" + " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n", bool_to_string(p_preset->get("user_data_backup/allow")), bool_to_string(p_preset->get("package/classify_as_game")), bool_to_string(p_preset->get("package/retain_data_on_uninstall")), bool_to_string(p_has_storage_permission)); + if (uses_xr) { + manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n"; + + bool hand_tracking_enabled = (int)(p_preset->get("xr_features/hand_tracking")) > XR_HAND_TRACKING_NONE; + if (hand_tracking_enabled) { + int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); + String hand_tracking_frequency = hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH"; + manifest_application_text += vformat( + " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.frequency\" android:value=\"%s\" />\n", + hand_tracking_frequency); + } + } manifest_application_text += _get_activity_tag(p_preset); manifest_application_text += " </application>\n"; return manifest_application_text; diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 8a93c25d79..3c440f3e29 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -44,6 +44,31 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; +// Supported XR modes. +// This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java' +static const int XR_MODE_REGULAR = 0; +static const int XR_MODE_OPENXR = 1; + +// Supported XR hand tracking modes. +static const int XR_HAND_TRACKING_NONE = 0; +static const int XR_HAND_TRACKING_OPTIONAL = 1; +static const int XR_HAND_TRACKING_REQUIRED = 2; + +// Supported XR hand tracking frequencies. +static const int XR_HAND_TRACKING_FREQUENCY_LOW = 0; +static const int XR_HAND_TRACKING_FREQUENCY_HIGH = 1; + +// Supported XR passthrough modes. +static const int XR_PASSTHROUGH_NONE = 0; +static const int XR_PASSTHROUGH_OPTIONAL = 1; +static const int XR_PASSTHROUGH_REQUIRED = 2; + +struct CustomExportData { + String assets_directory; + bool debug; + Vector<String> libs; +}; + int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation); String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 00e01884cf..3924aacccd 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -33,11 +33,24 @@ <!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. --> <!-- Do these changes in the export preset. Adding new ones is fine. --> + <!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. --> + <meta-data + android:name="xr_mode_metadata_name" + android:value="xr_mode_metadata_value" /> + + <!-- XR hand tracking metadata --> + <!-- This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. --> + <!-- Removed at export time if the xr mode is not VR or hand tracking is disabled. --> + <meta-data + android:name="xr_hand_tracking_metadata_name" + android:value="xr_hand_tracking_metadata_value"/> + <activity android:name=".GodotApp" android:label="@string/godot_project_name_string" android:theme="@style/GodotAppSplashTheme" android:launchMode="singleTask" + android:excludeFromRecents="false" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:resizeableActivity="false" @@ -49,6 +62,10 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> + + <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android + platforms. --> + <category android:name="com.oculus.intent.category.VR" /> </intent-filter> </activity> diff --git a/platform/android/java/app/assetPacks/installTime/build.gradle b/platform/android/java/app/assetPacks/installTime/build.gradle new file mode 100644 index 0000000000..b06faac374 --- /dev/null +++ b/platform/android/java/app/assetPacks/installTime/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'com.android.asset-pack' + +assetPack { + packName = "installTime" // Directory name for the asset pack + dynamicDelivery { + deliveryType = "install-time" // Delivery mode + } +} diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 18e07c3762..5d1a9d7b99 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -34,9 +34,8 @@ allprojects { } dependencies { - implementation libraries.supportCoreUtils implementation libraries.kotlinStdLib - implementation libraries.v4Support + implementation libraries.androidxFragment if (rootProject.findProject(":lib")) { implementation project(":lib") @@ -73,6 +72,8 @@ android { targetCompatibility versions.javaVersion } + assetPacks = [":assetPacks:installTime"] + defaultConfig { // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects. aaptOptions { @@ -90,8 +91,8 @@ android { applicationId getExportPackageName() versionCode getExportVersionCode() versionName getExportVersionName() - minSdkVersion versions.minSdk - targetSdkVersion versions.targetSdk + minSdkVersion getExportMinSdkVersion() + targetSdkVersion getExportTargetSdkVersion() } lintOptions { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index fad64c675f..32e03998da 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,23 +1,21 @@ ext.versions = [ - androidGradlePlugin: '4.2.2', + androidGradlePlugin: '7.0.3', compileSdk : 30, - minSdk : 19, - targetSdk : 30, + minSdk : 19, // Also update 'platform/android/java/lib/AndroidManifest.xml#minSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION' + targetSdk : 30, // Also update 'platform/android/java/lib/AndroidManifest.xml#targetSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION' buildTools : '30.0.3', - supportCoreUtils : '1.0.0', kotlinVersion : '1.5.10', - v4Support : '1.0.0', - javaVersion : 1.8, + fragmentVersion : '1.3.6', + javaVersion : 11, ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated. ] ext.libraries = [ androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin", - supportCoreUtils : "androidx.legacy:legacy-support-core-utils:$versions.supportCoreUtils", kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion", kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion", - v4Support : "androidx.legacy:legacy-support-v4:$versions.v4Support" + androidxFragment : "androidx.fragment:fragment:$versions.fragmentVersion", ] ext.getExportPackageName = { -> @@ -50,6 +48,30 @@ ext.getExportVersionName = { -> return versionName } +ext.getExportMinSdkVersion = { -> + String minSdkVersion = project.hasProperty("export_version_min_sdk") ? project.property("export_version_min_sdk") : "" + if (minSdkVersion == null || minSdkVersion.isEmpty()) { + minSdkVersion = "$versions.minSdk" + } + try { + return Integer.parseInt(minSdkVersion) + } catch (NumberFormatException ignored) { + return versions.minSdk + } +} + +ext.getExportTargetSdkVersion = { -> + String targetSdkVersion = project.hasProperty("export_version_target_sdk") ? project.property("export_version_target_sdk") : "" + if (targetSdkVersion == null || targetSdkVersion.isEmpty()) { + targetSdkVersion = "$versions.targetSdk" + } + try { + return Integer.parseInt(targetSdkVersion) + } catch (NumberFormatException ignored) { + return versions.targetSdk + } +} + ext.getGodotEditorVersion = { -> String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : "" if (editorVersion == null || editorVersion.isEmpty()) { diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle index 33b863c7bf..e38d7b2ba6 100644 --- a/platform/android/java/app/settings.gradle +++ b/platform/android/java/app/settings.gradle @@ -1,2 +1,2 @@ -// Empty settings.gradle file to denote this directory as being the root project -// of the Godot custom build. +// This is the root directory of the Godot custom build. +include ':assetPacks:installTime' diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 87bb2ea218..efdcc6c77b 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -158,9 +158,9 @@ def templateBuildTasks() { /** * Master task used to coordinate the tasks defined above to generate the set of Godot templates. */ -task generateGodotTemplates(type: GradleBuild) { - startParameter.excludedTaskNames = templateExcludedBuildTask() - tasks = templateBuildTasks() +task generateGodotTemplates { + gradle.startParameter.excludedTaskNames += templateExcludedBuildTask() + dependsOn = templateBuildTasks() finalizedBy 'zipCustomBuild' } @@ -168,12 +168,12 @@ task generateGodotTemplates(type: GradleBuild) { /** * Generates the same output as generateGodotTemplates but with dev symbols */ -task generateDevTemplate (type: GradleBuild) { +task generateDevTemplate { // add parameter to set symbols to true - startParameter.projectProperties += [doNotStrip: true] + gradle.startParameter.projectProperties += [doNotStrip: true] - startParameter.excludedTaskNames = templateExcludedBuildTask() - tasks = templateBuildTasks() + gradle.startParameter.excludedTaskNames += templateExcludedBuildTask() + dependsOn = templateBuildTasks() finalizedBy 'zipCustomBuild' } diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.jar b/platform/android/java/gradle/wrapper/gradle-wrapper.jar Binary files differindex f6b961fd5a..e708b1c023 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.jar +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.jar diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index 74c5636f8a..ffed3a254e 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Jun 23 23:42:22 PDT 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/platform/android/java/gradlew b/platform/android/java/gradlew index cccdd3d517..4f906e0c81 100755 --- a/platform/android/java/gradlew +++ b/platform/android/java/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat index 11cc30edb0..107acd32c4 100644 --- a/platform/android/java/gradlew.bat +++ b/platform/android/java/gradlew.bat @@ -1,7 +1,23 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem Gradle startup script for Windows @rem @rem ########################################################################## @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell @@ -75,7 +80,7 @@ if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index 3034794d69..2de62271c4 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -4,6 +4,9 @@ android:versionCode="1" android:versionName="1.0"> + <!-- Should match the mindSdk and targetSdk values in platform/android/java/app/config.gradle --> + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" /> + <application> <!-- Records the version of the Godot library --> diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 663ba73d40..fbed4ed078 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -2,9 +2,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' dependencies { - implementation libraries.supportCoreUtils implementation libraries.kotlinStdLib - implementation libraries.v4Support + implementation libraries.androidxFragment } def pathToRootDir = "../../../../" 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 896b169953..17ff3c75c0 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -119,7 +119,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC private Button mWiFiSettingsButton; private XRMode xrMode = XRMode.REGULAR; - private boolean use_32_bits = false; private boolean use_immersive = false; private boolean use_debug_opengl = false; private boolean mStatePaused; @@ -263,11 +262,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC GodotLib.setup(command_line); final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name"); - if (videoDriver.equals("Vulkan")) { + if (videoDriver.equals("vulkan")) { mRenderView = new GodotVulkanRenderView(activity, this); } else { - mRenderView = new GodotGLRenderView(activity, this, xrMode, use_32_bits, - use_debug_opengl); + mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl); } View view = mRenderView.getView(); @@ -295,7 +293,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { View pluginView = plugin.onMainCreate(activity); if (pluginView != null) { - containerLayout.addView(pluginView); + if (plugin.shouldBeOnTop()) { + containerLayout.addView(pluginView); + } else { + containerLayout.addView(pluginView, 0); + } } } } @@ -500,10 +502,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC boolean has_extra = i < command_line.length - 1; if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) { xrMode = XRMode.REGULAR; - } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) { - xrMode = XRMode.OVR; - } else if (command_line[i].equals("--use_depth_32")) { - use_32_bits = true; + } else if (command_line[i].equals(XRMode.OPENXR.cmdLineArg)) { + xrMode = XRMode.OPENXR; } else if (command_line[i].equals("--debug_opengl")) { use_debug_opengl = true; } else if (command_line[i].equals("--use_immersive")) { @@ -574,8 +574,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC if (!pack_valid) { Intent notifierIntent = new Intent(activity, activity.getClass()); - notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TOP); + notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java index d33faab641..09337ef989 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java @@ -50,9 +50,9 @@ public class GodotDownloaderService extends DownloaderService { }; /** - * This public key comes from your Android Market publisher account, and it - * used by the LVL to validate responses from Market on your behalf. - */ + * This public key comes from your Android Market publisher account, and it + * used by the LVL to validate responses from Market on your behalf. + */ @Override public String getPublicKey() { SharedPreferences prefs = getApplicationContext().getSharedPreferences("app_data_keys", Context.MODE_PRIVATE); @@ -63,20 +63,20 @@ public class GodotDownloaderService extends DownloaderService { } /** - * This is used by the preference obfuscater to make sure that your - * obfuscated preferences are different than the ones used by other - * applications. - */ + * This is used by the preference obfuscater to make sure that your + * obfuscated preferences are different than the ones used by other + * applications. + */ @Override public byte[] getSALT() { return SALT; } /** - * Fill this in with the class name for your alarm receiver. We do this - * because receivers must be unique across all of Android (it's a good idea - * to make sure that your receiver is in your unique package) - */ + * Fill this in with the class name for your alarm receiver. We do this + * because receivers must be unique across all of Android (it's a good idea + * to make sure that your receiver is in your unique package) + */ @Override public String getAlarmReceiverClassName() { Log.d("GODOT", "getAlarmReceiverClassName()"); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index a9d45c943b..d5b0b67903 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -78,10 +78,8 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView private final GodotRenderer godotRenderer; private PointerIcon pointerIcon; - public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits, - boolean p_use_debug_opengl) { + public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) { super(context); - GLUtils.use_32 = p_use_32_bits; GLUtils.use_debug_opengl = p_use_debug_opengl; this.godot = godot; @@ -91,7 +89,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); } - init(xrMode, false, 16, 0); + init(xrMode, false); } @Override @@ -172,11 +170,11 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView return pointerIcon; } - private void init(XRMode xrMode, boolean translucent, int depth, int stencil) { + private void init(XRMode xrMode, boolean translucent) { setPreserveEGLContextOnPause(true); setFocusableInTouchMode(true); switch (xrMode) { - case OVR: + case OPENXR: // Replace the default egl config chooser. setEGLConfigChooser(new OvrConfigChooser()); @@ -209,18 +207,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView * below. */ - if (GLUtils.use_32) { - setEGLConfigChooser(translucent ? - new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil, - new RegularConfigChooser(8, 8, 8, 8, 16, stencil)) : - new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil, - new RegularConfigChooser(5, 6, 5, 0, 16, stencil))); - - } else { - setEGLConfigChooser(translucent ? - new RegularConfigChooser(8, 8, 8, 8, 16, stencil) : - new RegularConfigChooser(5, 6, 5, 0, 16, stencil)); - } + setEGLConfigChooser( + new RegularFallbackConfigChooser(8, 8, 8, 8, 24, 0, + new RegularConfigChooser(8, 8, 8, 8, 16, 0))); break; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index d85d88ec6c..5f354b6b4c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -288,7 +288,34 @@ public class GodotIO { } public int getScreenOrientation() { - return activity.getRequestedOrientation(); + int orientation = activity.getRequestedOrientation(); + switch (orientation) { + case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: + return SCREEN_LANDSCAPE; + case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: + return SCREEN_PORTRAIT; + case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: + return SCREEN_REVERSE_LANDSCAPE; + case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: + return SCREEN_REVERSE_PORTRAIT; + case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: + case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: + return SCREEN_SENSOR_LANDSCAPE; + case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: + case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: + return SCREEN_SENSOR_PORTRAIT; + case ActivityInfo.SCREEN_ORIENTATION_SENSOR: + case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR: + case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: + return SCREEN_SENSOR; + case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: + case ActivityInfo.SCREEN_ORIENTATION_USER: + case ActivityInfo.SCREEN_ORIENTATION_BEHIND: + case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR: + case ActivityInfo.SCREEN_ORIENTATION_LOCKED: + default: + return -1; + } } public void setEdit(GodotEditText _edit) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 95870acda1..a23d030d4c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -75,9 +75,8 @@ public class GodotLib { /** * Invoked on the render thread when the underlying Android surface is created or recreated. * @param p_surface - * @param p_32_bits */ - public static native void newcontext(Surface p_surface, boolean p_32_bits); + public static native void newcontext(Surface p_surface); /** * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread. diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index 878a119c5c..12e452fc99 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -70,7 +70,7 @@ class GodotRenderer implements GLSurfaceView.Renderer { } public void onSurfaceCreated(GL10 gl, EGLConfig config) { - GodotLib.newcontext(null, GLUtils.use_32); + GodotLib.newcontext(null); for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onGLSurfaceCreated(gl, config); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index d1e8ae5ca9..a98ecad594 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 @@ -191,9 +191,9 @@ public class GodotEditText extends EditText { private boolean needHandlingInGodot(int keyCode, KeyEvent keyEvent) { boolean isArrowKey = keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || - keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT; + keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT; boolean isModifiedKey = keyEvent.isAltPressed() || keyEvent.isCtrlPressed() || keyEvent.isSymPressed() || - keyEvent.isFunctionPressed() || keyEvent.isMetaPressed(); + keyEvent.isFunctionPressed() || keyEvent.isMetaPressed(); return isArrowKey || keyCode == KeyEvent.KEYCODE_TAB || KeyEvent.isModifierKey(keyCode) || isModifiedKey; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index 2dc8359615..4536c21ed3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -191,6 +191,9 @@ public abstract class GodotPlugin { * The plugin can return a non-null {@link View} layout in order to add it to the Godot view * hierarchy. * + * Use shouldBeOnTop() to set whether the plugin's {@link View} should be added on top or behind + * the main Godot view. + * * @see Activity#onCreate(Bundle) * @return the plugin's view to be included; null if no views should be included. */ @@ -311,6 +314,17 @@ public abstract class GodotPlugin { } /** + * Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on + * top of the main Godot view. + * + * Returning false causes the plugin's {@link View} to be placed behind, which can be useful + * when used with transparency in order to let the Godot view handle inputs. + */ + public boolean shouldBeOnTop() { + return true; + } + + /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java index 19588f8465..09820fad5f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java @@ -44,7 +44,6 @@ public class GLUtils { public static final boolean DEBUG = false; - public static boolean use_32 = false; public static boolean use_debug_opengl = false; private static final String[] ATTRIBUTES_NAMES = new String[] { diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt index a35f6ec5a7..b13f9bfeab 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 @@ -58,7 +58,7 @@ internal class VkRenderer { * Called when the surface is created and signals the beginning of rendering. */ fun onVkSurfaceCreated(surface: Surface) { - GodotLib.newcontext(surface, false) + GodotLib.newcontext(surface) for (plugin in pluginRegistry.getAllPlugins()) { plugin.onVkSurfaceCreated(surface) diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java index 0995477baf..58f02b0396 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java @@ -35,7 +35,7 @@ package org.godotengine.godot.xr; */ public enum XRMode { REGULAR(0, "Regular", "--xr_mode_regular", "Default Android Gamepad"), // Regular/flatscreen - OVR(1, "Oculus Mobile VR", "--xr_mode_ovr", ""); + OPENXR(1, "OpenXR", "--xr_mode_openxr", ""); final int index; final String label; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java index e690c5b695..63c5381994 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java @@ -38,7 +38,7 @@ import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; -/* Fallback if 32bit View is not supported*/ +/* Fallback if the requested configuration is not supported */ public class RegularFallbackConfigChooser extends RegularConfigChooser { private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName(); @@ -55,7 +55,6 @@ public class RegularFallbackConfigChooser extends RegularConfigChooser { if (ec == null) { Log.w(TAG, "Trying ConfigChooser fallback"); ec = fallback.chooseConfig(egl, display, configs); - GLUtils.use_32 = false; } return ec; } diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle index 524031d93f..584b626900 100644 --- a/platform/android/java/settings.gradle +++ b/platform/android/java/settings.gradle @@ -4,3 +4,6 @@ rootProject.name = "Godot" include ':app' include ':lib' include ':nativeSrcsConfigs' + +include ':assetPacks:installTime' +project(':assetPacks:installTime').projectDir = file("app/assetPacks/installTime") diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index d971727269..3236512f5c 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -182,11 +182,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface) { if (os_android) { if (step.get() == 0) { // During startup - os_android->set_context_is_16_bits(!p_32_bits); if (p_surface) { ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface); os_android->set_native_window(native_window); @@ -318,8 +317,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env // Called on the UI thread JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) { - if (step.get() <= 0) + if (step.get() <= 0) { return; + } AndroidInputHandler::JoypadEvent jevent; jevent.device = p_device; @@ -332,24 +332,27 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, // Called on the UI thread JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) { - if (step.get() <= 0) + if (step.get() <= 0) { return; + } AndroidInputHandler::JoypadEvent jevent; jevent.device = p_device; jevent.type = AndroidInputHandler::JOY_EVENT_HAT; - int hat = 0; + HatMask hat = HatMask::CENTER; if (p_hat_x != 0) { - if (p_hat_x < 0) - hat |= HatMask::HAT_MASK_LEFT; - else - hat |= HatMask::HAT_MASK_RIGHT; + if (p_hat_x < 0) { + hat |= HatMask::LEFT; + } else { + hat |= HatMask::RIGHT; + } } if (p_hat_y != 0) { - if (p_hat_y < 0) - hat |= HatMask::HAT_MASK_UP; - else - hat |= HatMask::HAT_MASK_DOWN; + if (p_hat_y < 0) { + hat |= HatMask::UP; + } else { + hat |= HatMask::DOWN; + } } jevent.hat = hat; diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 63e9e6d8e5..7ea74480cb 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -41,7 +41,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface); 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); 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); diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differindex f44d360a25..9c8be93646 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 21fb31d991..ffd69a56b9 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -198,7 +198,7 @@ String OS_Android::get_resource_dir() const { String OS_Android::get_locale() const { String locale = godot_io_java->get_locale(); - if (locale != "") { + if (!locale.is_empty()) { return locale; } @@ -207,7 +207,7 @@ String OS_Android::get_locale() const { String OS_Android::get_model_name() const { String model = godot_io_java->get_model(); - if (model != "") + if (!model.is_empty()) return model; return OS_Unix::get_model_name(); @@ -218,11 +218,11 @@ String OS_Android::get_data_path() const { } String OS_Android::get_user_data_dir() const { - if (data_dir_cache != String()) + if (!data_dir_cache.is_empty()) return data_dir_cache; String data_dir = godot_io_java->get_user_data_dir(); - if (data_dir != "") { + if (!data_dir.is_empty()) { data_dir_cache = _remove_symlink(data_dir); return data_dir_cache; } @@ -230,17 +230,20 @@ String OS_Android::get_user_data_dir() const { } String OS_Android::get_cache_path() const { + if (!cache_dir_cache.is_empty()) + return cache_dir_cache; + String cache_dir = godot_io_java->get_cache_dir(); - if (cache_dir != "") { - cache_dir = _remove_symlink(cache_dir); - return cache_dir; + if (!cache_dir.is_empty()) { + cache_dir_cache = _remove_symlink(cache_dir); + return cache_dir_cache; } return "."; } String OS_Android::get_unique_id() const { String unique_id = godot_io_java->get_unique_id(); - if (unique_id != "") + if (!unique_id.is_empty()) return unique_id; return OS::get_unique_id(); @@ -258,16 +261,8 @@ Size2i OS_Android::get_display_size() const { return display_size; } -void OS_Android::set_context_is_16_bits(bool p_is_16) { -#if defined(OPENGL_ENABLED) - //use_16bits_fbo = p_is_16; - //if (rasterizer) - // rasterizer->set_force_16_bits_fbo(p_is_16); -#endif -} - void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { -#if defined(OPENGL_ENABLED) +#if defined(GLES3_ENABLED) ERR_FAIL_COND(!p_gl_extensions); gl_extensions = p_gl_extensions; #endif @@ -319,10 +314,9 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god main_loop = nullptr; -#if defined(OPENGL_ENABLED) +#if defined(GLES3_ENABLED) gl_extensions = nullptr; use_gl2 = false; - use_16bits_fbo = false; #endif #if defined(VULKAN_ENABLED) diff --git a/platform/android/os_android.h b/platform/android/os_android.h index c938297821..a62f79952c 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -47,8 +47,7 @@ private: bool use_apk_expansion; -#if defined(OPENGL_ENABLED) - bool use_16bits_fbo; +#if defined(GLES3_ENABLED) const char *gl_extensions; #endif @@ -57,6 +56,7 @@ private: #endif mutable String data_dir_cache; + mutable String cache_dir_cache; AudioDriverOpenSL audio_driver_android; @@ -102,7 +102,6 @@ public: void set_display_size(const Size2i &p_size); Size2i get_display_size() const; - void set_context_is_16_bits(bool p_is_16); void set_opengl_extensions(const char *p_gl_extensions); void set_native_window(ANativeWindow *p_native_window); |