initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
30
platform/SCsub
Normal file
30
platform/SCsub
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
from glob import glob
|
||||
|
||||
import platform_builders
|
||||
|
||||
Import("env")
|
||||
|
||||
env.platform_sources = []
|
||||
|
||||
|
||||
# Generate export icons
|
||||
for platform in env.platform_exporters:
|
||||
for path in glob(f"{platform}/export/*.svg"):
|
||||
env.CommandNoCache(path.replace(".svg", "_svg.gen.h"), path, env.Run(platform_builders.export_icon_builder))
|
||||
|
||||
|
||||
# Register platform-exclusive APIs
|
||||
register_platform_apis = env.CommandNoCache(
|
||||
"register_platform_apis.gen.cpp",
|
||||
env.Value(env.platform_apis),
|
||||
env.Run(platform_builders.register_platform_apis_builder),
|
||||
)
|
||||
env.add_source_files(env.platform_sources, register_platform_apis)
|
||||
for platform in env.platform_apis:
|
||||
env.add_source_files(env.platform_sources, f"{platform}/api/*.cpp")
|
||||
|
||||
lib = env.add_library("platform", env.platform_sources)
|
||||
env.Prepend(LIBS=[lib])
|
2
platform/android/.editorconfig
Normal file
2
platform/android/.editorconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
[{*.gradle,AndroidManifest.xml}]
|
||||
indent_style = space
|
21
platform/android/README.md
Normal file
21
platform/android/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Compiling for Android](https://docs.godotengine.org/en/latest/engine_details/development/compiling/compiling_for_android.html)
|
||||
- Instructions on building this platform port from source.
|
||||
- [Exporting for Android](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_android.html)
|
||||
- Instructions on using the compiled export templates to export a project.
|
||||
|
||||
## Artwork license
|
||||
|
||||
[`logo.svg`](export/logo.svg) and [`run_icon.svg`](export/run_icon.svg) 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.
|
120
platform/android/SCsub
Normal file
120
platform/android/SCsub
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import platform_android_builders
|
||||
|
||||
from methods import print_warning
|
||||
|
||||
Import("env")
|
||||
|
||||
android_files = [
|
||||
"os_android.cpp",
|
||||
"android_input_handler.cpp",
|
||||
"file_access_android.cpp",
|
||||
"file_access_filesystem_jandroid.cpp",
|
||||
"audio_driver_opensl.cpp",
|
||||
"dir_access_jandroid.cpp",
|
||||
"tts_android.cpp",
|
||||
"thread_jandroid.cpp",
|
||||
"net_socket_android.cpp",
|
||||
"java_godot_lib_jni.cpp",
|
||||
"java_class_wrapper.cpp",
|
||||
"java_godot_wrapper.cpp",
|
||||
"java_godot_view_wrapper.cpp",
|
||||
"java_godot_io_wrapper.cpp",
|
||||
"jni_utils.cpp",
|
||||
"android_keys_utils.cpp",
|
||||
"display_server_android.cpp",
|
||||
"plugin/godot_plugin_jni.cpp",
|
||||
"rendering_context_driver_vulkan_android.cpp",
|
||||
"variant/callable_jni.cpp",
|
||||
"dialog_utils_jni.cpp",
|
||||
"editor/game_menu_utils_jni.cpp",
|
||||
"editor/editor_utils_jni.cpp",
|
||||
]
|
||||
|
||||
env_android = env.Clone()
|
||||
|
||||
android_objects = []
|
||||
for x in android_files:
|
||||
android_objects.append(env_android.SharedObject(x))
|
||||
|
||||
env_thirdparty = env_android.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
thirdparty_obj = env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc")
|
||||
android_objects.append(thirdparty_obj)
|
||||
|
||||
# FIXME: Hardcoded to bin to ensure the directory exists if not redirecting build objects.
|
||||
lib = env_android.add_shared_library("#bin/libgodot", android_objects, redirect_build_objects=False)
|
||||
|
||||
# Needed to force rebuilding the platform files when the thirdparty code is updated.
|
||||
env.Depends(lib, thirdparty_obj)
|
||||
|
||||
lib_arch_dir = ""
|
||||
triple_target_dir = ""
|
||||
if env["arch"] == "arm32":
|
||||
lib_arch_dir = "armeabi-v7a"
|
||||
triple_target_dir = "arm-linux-androideabi"
|
||||
elif env["arch"] == "arm64":
|
||||
lib_arch_dir = "arm64-v8a"
|
||||
triple_target_dir = "aarch64-linux-android"
|
||||
elif env["arch"] == "x86_32":
|
||||
lib_arch_dir = "x86"
|
||||
triple_target_dir = "i686-linux-android"
|
||||
elif env["arch"] == "x86_64":
|
||||
lib_arch_dir = "x86_64"
|
||||
triple_target_dir = "x86_64-linux-android"
|
||||
else:
|
||||
print_warning("Architecture not suitable for embedding into APK; keeping .so at \\bin")
|
||||
|
||||
host_subpath = ""
|
||||
if sys.platform.startswith("linux"):
|
||||
host_subpath = "linux-x86_64"
|
||||
elif sys.platform.startswith("darwin"):
|
||||
host_subpath = "darwin-x86_64"
|
||||
elif sys.platform.startswith("win"):
|
||||
if platform.machine().endswith("64"):
|
||||
host_subpath = "windows-x86_64"
|
||||
else:
|
||||
host_subpath = "windows"
|
||||
|
||||
if lib_arch_dir != "" and host_subpath != "":
|
||||
if env.dev_build:
|
||||
lib_type_dir = "dev"
|
||||
elif env.debug_features:
|
||||
if env.editor_build and env["store_release"]:
|
||||
lib_type_dir = "release"
|
||||
else:
|
||||
lib_type_dir = "debug"
|
||||
else: # Release
|
||||
lib_type_dir = "release"
|
||||
|
||||
if env.editor_build:
|
||||
lib_tools_dir = "tools/"
|
||||
else:
|
||||
lib_tools_dir = ""
|
||||
|
||||
jni_libs_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/"
|
||||
out_dir = jni_libs_dir + lib_arch_dir
|
||||
env_android.CommandNoCache(out_dir + "/libgodot_android.so", lib, Move("$TARGET", "$SOURCE"))
|
||||
|
||||
stl_lib_path = f"{env['ANDROID_NDK_ROOT']}/toolchains/llvm/prebuilt/{host_subpath}/sysroot/usr/lib/{triple_target_dir}/libc++_shared.so"
|
||||
env_android.CommandNoCache(out_dir + "/libc++_shared.so", stl_lib_path, Copy("$TARGET", "$SOURCE"))
|
||||
|
||||
if env["debug_symbols"] and env["separate_debug_symbols"]:
|
||||
debug_symbols_zip_file = (
|
||||
("#bin/android-editor-" if env.editor_build else "#bin/android-template-")
|
||||
+ lib_type_dir
|
||||
+ "-native-symbols.zip"
|
||||
)
|
||||
env_android.NoCache(Zip(debug_symbols_zip_file, jni_libs_dir, ZIPROOT=Dir(jni_libs_dir)))
|
||||
|
||||
if env["generate_android_binaries"]:
|
||||
env_android.AlwaysBuild(
|
||||
env_android.CommandNoCache(
|
||||
"generate_android_binaries", lib, env.Run(platform_android_builders.generate_android_binaries)
|
||||
)
|
||||
)
|
461
platform/android/android_input_handler.cpp
Normal file
461
platform/android/android_input_handler.cpp
Normal file
@@ -0,0 +1,461 @@
|
||||
/**************************************************************************/
|
||||
/* android_input_handler.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "android_input_handler.h"
|
||||
|
||||
#include "android_keys_utils.h"
|
||||
#include "display_server_android.h"
|
||||
|
||||
void AndroidInputHandler::process_joy_event(AndroidInputHandler::JoypadEvent p_event) {
|
||||
switch (p_event.type) {
|
||||
case JOY_EVENT_BUTTON:
|
||||
Input::get_singleton()->joy_button(p_event.device, (JoyButton)p_event.index, p_event.pressed);
|
||||
break;
|
||||
case JOY_EVENT_AXIS:
|
||||
Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, p_event.value);
|
||||
break;
|
||||
case JOY_EVENT_HAT:
|
||||
Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> ev, Key p_keycode) {
|
||||
if (p_keycode != Key::SHIFT) {
|
||||
ev->set_shift_pressed(shift_mem);
|
||||
}
|
||||
if (p_keycode != Key::ALT) {
|
||||
ev->set_alt_pressed(alt_mem);
|
||||
}
|
||||
if (p_keycode != Key::META) {
|
||||
ev->set_meta_pressed(meta_mem);
|
||||
}
|
||||
if (p_keycode != Key::CTRL) {
|
||||
ev->set_ctrl_pressed(control_mem);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed, bool p_echo) {
|
||||
static char32_t prev_wc = 0;
|
||||
char32_t unicode = p_unicode;
|
||||
if ((p_unicode & 0xfffffc00) == 0xd800) {
|
||||
if (prev_wc != 0) {
|
||||
ERR_PRINT("invalid utf16 surrogate input");
|
||||
}
|
||||
prev_wc = unicode;
|
||||
return; // Skip surrogate.
|
||||
} else if ((unicode & 0xfffffc00) == 0xdc00) {
|
||||
if (prev_wc == 0) {
|
||||
ERR_PRINT("invalid utf16 surrogate input");
|
||||
return; // Skip invalid surrogate.
|
||||
}
|
||||
unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
|
||||
prev_wc = 0;
|
||||
} else {
|
||||
prev_wc = 0;
|
||||
}
|
||||
|
||||
Ref<InputEventKey> ev;
|
||||
ev.instantiate();
|
||||
|
||||
Key physical_keycode = godot_code_from_android_code(p_physical_keycode);
|
||||
Key keycode;
|
||||
if (unicode == '\b') { // 0x08
|
||||
keycode = Key::BACKSPACE;
|
||||
} else if (unicode == '\t') { // 0x09
|
||||
keycode = Key::TAB;
|
||||
} else if (unicode == '\n') { // 0x0A
|
||||
keycode = Key::ENTER;
|
||||
} else if (unicode == 0x1B) {
|
||||
keycode = Key::ESCAPE;
|
||||
} else if (unicode == 0x1F) {
|
||||
keycode = Key::KEY_DELETE;
|
||||
} else {
|
||||
keycode = fix_keycode(unicode, physical_keycode);
|
||||
}
|
||||
|
||||
switch (physical_keycode) {
|
||||
case Key::SHIFT: {
|
||||
shift_mem = p_pressed;
|
||||
} break;
|
||||
case Key::ALT: {
|
||||
alt_mem = p_pressed;
|
||||
} break;
|
||||
case Key::CTRL: {
|
||||
control_mem = p_pressed;
|
||||
} break;
|
||||
case Key::META: {
|
||||
meta_mem = p_pressed;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ev->set_keycode(keycode);
|
||||
ev->set_physical_keycode(physical_keycode);
|
||||
ev->set_key_label(fix_key_label(p_key_label, keycode));
|
||||
ev->set_unicode(fix_unicode(unicode));
|
||||
ev->set_location(godot_location_from_android_code(p_physical_keycode));
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_echo(p_echo);
|
||||
|
||||
_set_key_modifier_state(ev, keycode);
|
||||
|
||||
if (p_physical_keycode == AKEYCODE_BACK && p_pressed) {
|
||||
if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) {
|
||||
dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST, true);
|
||||
}
|
||||
}
|
||||
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_cancel_all_touch() {
|
||||
_parse_all_touch(false, true);
|
||||
touch.clear();
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_canceled, bool p_double_tap) {
|
||||
if (touch.size()) {
|
||||
//end all if exist
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instantiate();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_canceled(p_canceled);
|
||||
ev->set_position(touch[i].pos);
|
||||
ev->set_double_tap(p_double_tap);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_release_all_touch() {
|
||||
_parse_all_touch(false, false);
|
||||
touch.clear();
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap) {
|
||||
switch (p_event) {
|
||||
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
|
||||
// Release any remaining touches or mouse event
|
||||
_release_mouse_event_info();
|
||||
_release_all_touch();
|
||||
|
||||
touch.resize(p_points.size());
|
||||
for (int i = 0; i < p_points.size(); i++) {
|
||||
touch.write[i].id = p_points[i].id;
|
||||
touch.write[i].pos = p_points[i].pos;
|
||||
touch.write[i].pressure = p_points[i].pressure;
|
||||
touch.write[i].tilt = p_points[i].tilt;
|
||||
}
|
||||
|
||||
//send touch
|
||||
_parse_all_touch(true, false, p_double_tap);
|
||||
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_MOVE: { //motion
|
||||
if (touch.size() != p_points.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
int idx = -1;
|
||||
for (int j = 0; j < p_points.size(); j++) {
|
||||
if (touch[i].id == p_points[j].id) {
|
||||
idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_CONTINUE(idx == -1);
|
||||
|
||||
if (touch[i].pos == p_points[idx].pos) {
|
||||
continue; // Don't move unnecessarily.
|
||||
}
|
||||
|
||||
Ref<InputEventScreenDrag> ev;
|
||||
ev.instantiate();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_position(p_points[idx].pos);
|
||||
ev->set_relative(p_points[idx].pos - touch[i].pos);
|
||||
ev->set_relative_screen_position(ev->get_relative());
|
||||
ev->set_pressure(p_points[idx].pressure);
|
||||
ev->set_tilt(p_points[idx].tilt);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
touch.write[i].pos = p_points[idx].pos;
|
||||
}
|
||||
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_CANCEL: {
|
||||
_cancel_all_touch();
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_UP: { //release
|
||||
_release_all_touch();
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
|
||||
for (int i = 0; i < p_points.size(); i++) {
|
||||
if (p_points[i].id == p_pointer) {
|
||||
TouchPos tp = p_points[i];
|
||||
touch.push_back(tp);
|
||||
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instantiate();
|
||||
|
||||
ev->set_index(tp.id);
|
||||
ev->set_pressed(true);
|
||||
ev->set_position(tp.pos);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_UP: { // remove touch
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
if (touch[i].id == p_pointer) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instantiate();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_pressed(false);
|
||||
ev->set_position(touch[i].pos);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
touch.remove_at(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_cancel_mouse_event_info(bool p_source_mouse_relative) {
|
||||
buttons_state = BitField<MouseButtonMask>();
|
||||
_parse_mouse_event_info(BitField<MouseButtonMask>(), false, true, false, p_source_mouse_relative);
|
||||
mouse_event_info.valid = false;
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative) {
|
||||
if (!mouse_event_info.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> ev;
|
||||
ev.instantiate();
|
||||
_set_key_modifier_state(ev, Key::NONE);
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
} else {
|
||||
ev->set_position(mouse_event_info.pos);
|
||||
ev->set_global_position(mouse_event_info.pos);
|
||||
hover_prev_pos = mouse_event_info.pos;
|
||||
}
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_canceled(p_canceled);
|
||||
BitField<MouseButtonMask> changed_button_mask = buttons_state.get_different(event_buttons_mask);
|
||||
|
||||
buttons_state = event_buttons_mask;
|
||||
|
||||
ev->set_button_index(_button_index_from_mask(changed_button_mask));
|
||||
ev->set_button_mask(event_buttons_mask);
|
||||
ev->set_double_click(p_double_click);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
|
||||
_parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, false, p_source_mouse_relative);
|
||||
mouse_event_info.valid = false;
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt) {
|
||||
BitField<MouseButtonMask> event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
|
||||
switch (p_event_action) {
|
||||
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
|
||||
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
|
||||
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
|
||||
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
|
||||
Ref<InputEventMouseMotion> ev;
|
||||
ev.instantiate();
|
||||
_set_key_modifier_state(ev, Key::NONE);
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
ev->set_relative(p_event_pos - hover_prev_pos);
|
||||
ev->set_relative_screen_position(ev->get_relative());
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
hover_prev_pos = p_event_pos;
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
|
||||
// Release any remaining touches or mouse event
|
||||
_release_mouse_event_info();
|
||||
_release_all_touch();
|
||||
|
||||
mouse_event_info.valid = true;
|
||||
mouse_event_info.pos = p_event_pos;
|
||||
_parse_mouse_event_info(event_buttons_mask, true, false, p_double_click, p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_CANCEL: {
|
||||
_cancel_mouse_event_info(p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_UP:
|
||||
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
|
||||
_release_mouse_event_info(p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_MOVE: {
|
||||
if (!p_source_mouse_relative && !mouse_event_info.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> ev;
|
||||
ev.instantiate();
|
||||
_set_key_modifier_state(ev, Key::NONE);
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
ev->set_relative(p_event_pos);
|
||||
ev->set_relative_screen_position(p_event_pos);
|
||||
} else {
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
ev->set_relative(p_event_pos - hover_prev_pos);
|
||||
ev->set_relative_screen_position(ev->get_relative());
|
||||
mouse_event_info.pos = p_event_pos;
|
||||
hover_prev_pos = p_event_pos;
|
||||
}
|
||||
ev->set_button_mask(event_buttons_mask);
|
||||
ev->set_pressure(p_pressure);
|
||||
ev->set_tilt(p_tilt);
|
||||
Input::get_singleton()->parse_input_event(ev);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_SCROLL: {
|
||||
Ref<InputEventMouseButton> ev;
|
||||
ev.instantiate();
|
||||
_set_key_modifier_state(ev, Key::NONE);
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
} else {
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
}
|
||||
ev->set_pressed(true);
|
||||
buttons_state = event_buttons_mask;
|
||||
if (p_delta.y > 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, p_delta.y);
|
||||
} else if (p_delta.y < 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -p_delta.y);
|
||||
}
|
||||
|
||||
if (p_delta.x > 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, p_delta.x);
|
||||
} else if (p_delta.x < 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -p_delta.x);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
|
||||
Ref<InputEventMouseButton> evd = ev->duplicate();
|
||||
_set_key_modifier_state(evd, Key::NONE);
|
||||
evd->set_button_index(wheel_button);
|
||||
evd->set_button_mask(event_buttons_mask.get_different(mouse_button_to_mask(wheel_button)));
|
||||
evd->set_factor(factor);
|
||||
Input::get_singleton()->parse_input_event(evd);
|
||||
Ref<InputEventMouseButton> evdd = evd->duplicate();
|
||||
evdd->set_pressed(false);
|
||||
evdd->set_button_mask(event_buttons_mask);
|
||||
Input::get_singleton()->parse_input_event(evdd);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
|
||||
Ref<InputEventMagnifyGesture> magnify_event;
|
||||
magnify_event.instantiate();
|
||||
_set_key_modifier_state(magnify_event, Key::NONE);
|
||||
magnify_event->set_position(p_pos);
|
||||
magnify_event->set_factor(p_factor);
|
||||
Input::get_singleton()->parse_input_event(magnify_event);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
|
||||
Ref<InputEventPanGesture> pan_event;
|
||||
pan_event.instantiate();
|
||||
_set_key_modifier_state(pan_event, Key::NONE);
|
||||
pan_event->set_position(p_pos);
|
||||
pan_event->set_delta(p_delta);
|
||||
Input::get_singleton()->parse_input_event(pan_event);
|
||||
}
|
||||
|
||||
MouseButton AndroidInputHandler::_button_index_from_mask(BitField<MouseButtonMask> button_mask) {
|
||||
switch (button_mask) {
|
||||
case MouseButtonMask::LEFT:
|
||||
return MouseButton::LEFT;
|
||||
case MouseButtonMask::RIGHT:
|
||||
return MouseButton::RIGHT;
|
||||
case MouseButtonMask::MIDDLE:
|
||||
return MouseButton::MIDDLE;
|
||||
case MouseButtonMask::MB_XBUTTON1:
|
||||
return MouseButton::MB_XBUTTON1;
|
||||
case MouseButtonMask::MB_XBUTTON2:
|
||||
return MouseButton::MB_XBUTTON2;
|
||||
default:
|
||||
return MouseButton::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
BitField<MouseButtonMask> AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
|
||||
BitField<MouseButtonMask> godot_button_mask = MouseButtonMask::NONE;
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::LEFT);
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::RIGHT);
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::MIDDLE);
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON1);
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON2);
|
||||
}
|
||||
|
||||
return godot_button_mask;
|
||||
}
|
106
platform/android/android_input_handler.h
Normal file
106
platform/android/android_input_handler.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/**************************************************************************/
|
||||
/* android_input_handler.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input.h"
|
||||
|
||||
// This class encapsulates all the handling of input events that come from the Android UI thread.
|
||||
// Remarks:
|
||||
// - It's not thread-safe by itself, so its functions must only be called on a single thread, which is the Android UI thread.
|
||||
// - Its functions must only call thread-safe methods.
|
||||
class AndroidInputHandler {
|
||||
public:
|
||||
struct TouchPos {
|
||||
int id = 0;
|
||||
Point2 pos;
|
||||
float pressure = 0;
|
||||
Vector2 tilt;
|
||||
};
|
||||
|
||||
struct MouseEventInfo {
|
||||
bool valid = false;
|
||||
Point2 pos;
|
||||
};
|
||||
|
||||
enum {
|
||||
JOY_EVENT_BUTTON = 0,
|
||||
JOY_EVENT_AXIS = 1,
|
||||
JOY_EVENT_HAT = 2
|
||||
};
|
||||
|
||||
struct JoypadEvent {
|
||||
int device = 0;
|
||||
int type = 0;
|
||||
int index = 0; // Can be either JoyAxis or JoyButton.
|
||||
bool pressed = false;
|
||||
float value = 0;
|
||||
BitField<HatMask> hat = HatMask::CENTER;
|
||||
};
|
||||
|
||||
private:
|
||||
bool alt_mem = false;
|
||||
bool shift_mem = false;
|
||||
bool control_mem = false;
|
||||
bool meta_mem = false;
|
||||
|
||||
BitField<MouseButtonMask> buttons_state = MouseButtonMask::NONE;
|
||||
|
||||
Vector<TouchPos> touch;
|
||||
MouseEventInfo mouse_event_info;
|
||||
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
|
||||
|
||||
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev, Key p_keycode);
|
||||
|
||||
static MouseButton _button_index_from_mask(BitField<MouseButtonMask> button_mask);
|
||||
static BitField<MouseButtonMask> _android_button_mask_to_godot_button_mask(int android_button_mask);
|
||||
|
||||
void _wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
|
||||
|
||||
void _parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative);
|
||||
|
||||
void _release_mouse_event_info(bool p_source_mouse_relative = false);
|
||||
|
||||
void _cancel_mouse_event_info(bool p_source_mouse_relative = false);
|
||||
|
||||
void _parse_all_touch(bool p_pressed, bool p_canceled = false, bool p_double_tap = false);
|
||||
|
||||
void _release_all_touch();
|
||||
|
||||
void _cancel_all_touch();
|
||||
|
||||
public:
|
||||
void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt);
|
||||
void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap);
|
||||
void process_magnify(Point2 p_pos, float p_factor);
|
||||
void process_pan(Point2 p_pos, Vector2 p_delta);
|
||||
void process_joy_event(JoypadEvent p_event);
|
||||
void process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed, bool p_echo);
|
||||
};
|
49
platform/android/android_keys_utils.cpp
Normal file
49
platform/android/android_keys_utils.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/**************************************************************************/
|
||||
/* android_keys_utils.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "android_keys_utils.h"
|
||||
|
||||
Key godot_code_from_android_code(unsigned int p_code) {
|
||||
for (int i = 0; android_godot_code_pairs[i].android_code != AKEYCODE_MAX; i++) {
|
||||
if (android_godot_code_pairs[i].android_code == p_code) {
|
||||
return android_godot_code_pairs[i].godot_code;
|
||||
}
|
||||
}
|
||||
return Key::UNKNOWN;
|
||||
}
|
||||
|
||||
KeyLocation godot_location_from_android_code(unsigned int p_code) {
|
||||
for (int i = 0; android_godot_location_pairs[i].android_code != AKEYCODE_MAX; i++) {
|
||||
if (android_godot_location_pairs[i].android_code == p_code) {
|
||||
return android_godot_location_pairs[i].godot_code;
|
||||
}
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
197
platform/android/android_keys_utils.h
Normal file
197
platform/android/android_keys_utils.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/**************************************************************************/
|
||||
/* android_keys_utils.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
#include <android/input.h>
|
||||
|
||||
#define AKEYCODE_MAX 0xFFFF
|
||||
|
||||
struct AndroidGodotCodePair {
|
||||
unsigned int android_code = 0;
|
||||
Key godot_code = Key::NONE;
|
||||
};
|
||||
|
||||
static AndroidGodotCodePair android_godot_code_pairs[] = {
|
||||
{ AKEYCODE_UNKNOWN, Key::UNKNOWN }, // (0) Unknown key code.
|
||||
{ AKEYCODE_BACK, Key::BACK }, // (4) Back key.
|
||||
{ AKEYCODE_0, Key::KEY_0 }, // (7) '0' key.
|
||||
{ AKEYCODE_1, Key::KEY_1 }, // (8) '1' key.
|
||||
{ AKEYCODE_2, Key::KEY_2 }, // (9) '2' key.
|
||||
{ AKEYCODE_3, Key::KEY_3 }, // (10) '3' key.
|
||||
{ AKEYCODE_4, Key::KEY_4 }, // (11) '4' key.
|
||||
{ AKEYCODE_5, Key::KEY_5 }, // (12) '5' key.
|
||||
{ AKEYCODE_6, Key::KEY_6 }, // (13) '6' key.
|
||||
{ AKEYCODE_7, Key::KEY_7 }, // (14) '7' key.
|
||||
{ AKEYCODE_8, Key::KEY_8 }, // (15) '8' key.
|
||||
{ AKEYCODE_9, Key::KEY_9 }, // (16) '9' key.
|
||||
{ AKEYCODE_STAR, Key::ASTERISK }, // (17) '*' key.
|
||||
{ AKEYCODE_POUND, Key::NUMBERSIGN }, // (18) '#' key.
|
||||
{ AKEYCODE_DPAD_UP, Key::UP }, // (19) Directional Pad Up key.
|
||||
{ AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key.
|
||||
{ AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key.
|
||||
{ AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key.
|
||||
{ AKEYCODE_DPAD_CENTER, Key::ENTER }, // (23) Directional Pad Center key.
|
||||
{ AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key.
|
||||
{ AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key.
|
||||
{ AKEYCODE_POWER, Key::STANDBY }, // (26) Power key.
|
||||
{ AKEYCODE_CLEAR, Key::CLEAR }, // (28) Clear key.
|
||||
{ AKEYCODE_A, Key::A }, // (29) 'A' key.
|
||||
{ AKEYCODE_B, Key::B }, // (30) 'B' key.
|
||||
{ AKEYCODE_C, Key::C }, // (31) 'C' key.
|
||||
{ AKEYCODE_D, Key::D }, // (32) 'D' key.
|
||||
{ AKEYCODE_E, Key::E }, // (33) 'E' key.
|
||||
{ AKEYCODE_F, Key::F }, // (34) 'F' key.
|
||||
{ AKEYCODE_G, Key::G }, // (35) 'G' key.
|
||||
{ AKEYCODE_H, Key::H }, // (36) 'H' key.
|
||||
{ AKEYCODE_I, Key::I }, // (37) 'I' key.
|
||||
{ AKEYCODE_J, Key::J }, // (38) 'J' key.
|
||||
{ AKEYCODE_K, Key::K }, // (39) 'K' key.
|
||||
{ AKEYCODE_L, Key::L }, // (40) 'L' key.
|
||||
{ AKEYCODE_M, Key::M }, // (41) 'M' key.
|
||||
{ AKEYCODE_N, Key::N }, // (42) 'N' key.
|
||||
{ AKEYCODE_O, Key::O }, // (43) 'O' key.
|
||||
{ AKEYCODE_P, Key::P }, // (44) 'P' key.
|
||||
{ AKEYCODE_Q, Key::Q }, // (45) 'Q' key.
|
||||
{ AKEYCODE_R, Key::R }, // (46) 'R' key.
|
||||
{ AKEYCODE_S, Key::S }, // (47) 'S' key.
|
||||
{ AKEYCODE_T, Key::T }, // (48) 'T' key.
|
||||
{ AKEYCODE_U, Key::U }, // (49) 'U' key.
|
||||
{ AKEYCODE_V, Key::V }, // (50) 'V' key.
|
||||
{ AKEYCODE_W, Key::W }, // (51) 'W' key.
|
||||
{ AKEYCODE_X, Key::X }, // (52) 'X' key.
|
||||
{ AKEYCODE_Y, Key::Y }, // (53) 'Y' key.
|
||||
{ AKEYCODE_Z, Key::Z }, // (54) 'Z' key.
|
||||
{ AKEYCODE_COMMA, Key::COMMA }, // (55) ',’ key.
|
||||
{ AKEYCODE_PERIOD, Key::PERIOD }, // (56) '.' key.
|
||||
{ AKEYCODE_ALT_LEFT, Key::ALT }, // (57) Left Alt modifier key.
|
||||
{ AKEYCODE_ALT_RIGHT, Key::ALT }, // (58) Right Alt modifier key.
|
||||
{ AKEYCODE_SHIFT_LEFT, Key::SHIFT }, // (59) Left Shift modifier key.
|
||||
{ AKEYCODE_SHIFT_RIGHT, Key::SHIFT }, // (60) Right Shift modifier key.
|
||||
{ AKEYCODE_TAB, Key::TAB }, // (61) Tab key.
|
||||
{ AKEYCODE_SPACE, Key::SPACE }, // (62) Space key.
|
||||
{ AKEYCODE_ENVELOPE, Key::LAUNCHMAIL }, // (65) Envelope special function key.
|
||||
{ AKEYCODE_ENTER, Key::ENTER }, // (66) Enter key.
|
||||
{ AKEYCODE_DEL, Key::BACKSPACE }, // (67) Backspace key.
|
||||
{ AKEYCODE_GRAVE, Key::QUOTELEFT }, // (68) '`' (backtick) key.
|
||||
{ AKEYCODE_MINUS, Key::MINUS }, // (69) '-'.
|
||||
{ AKEYCODE_EQUALS, Key::EQUAL }, // (70) '=' key.
|
||||
{ AKEYCODE_LEFT_BRACKET, Key::BRACKETLEFT }, // (71) '[' key.
|
||||
{ AKEYCODE_RIGHT_BRACKET, Key::BRACKETRIGHT }, // (72) ']' key.
|
||||
{ AKEYCODE_BACKSLASH, Key::BACKSLASH }, // (73) '\' key.
|
||||
{ AKEYCODE_SEMICOLON, Key::SEMICOLON }, // (74) ';' key.
|
||||
{ AKEYCODE_APOSTROPHE, Key::APOSTROPHE }, // (75) ''' (apostrophe) key.
|
||||
{ AKEYCODE_SLASH, Key::SLASH }, // (76) '/' key.
|
||||
{ AKEYCODE_AT, Key::AT }, // (77) '@' key.
|
||||
{ AKEYCODE_PLUS, Key::PLUS }, // (81) '+' key.
|
||||
{ AKEYCODE_MENU, Key::MENU }, // (82) Menu key.
|
||||
{ AKEYCODE_SEARCH, Key::SEARCH }, // (84) Search key.
|
||||
{ AKEYCODE_MEDIA_STOP, Key::MEDIASTOP }, // (86) Stop media key.
|
||||
{ AKEYCODE_MEDIA_NEXT, Key::MEDIANEXT }, // (87) Play Next media key.
|
||||
{ AKEYCODE_MEDIA_PREVIOUS, Key::MEDIAPREVIOUS }, // (88) Play Previous media key.
|
||||
{ AKEYCODE_PAGE_UP, Key::PAGEUP }, // (92) Page Up key.
|
||||
{ AKEYCODE_PAGE_DOWN, Key::PAGEDOWN }, // (93) Page Down key.
|
||||
{ AKEYCODE_ESCAPE, Key::ESCAPE }, // (111) Escape key.
|
||||
{ AKEYCODE_FORWARD_DEL, Key::KEY_DELETE }, // (112) Forward Delete key.
|
||||
{ AKEYCODE_CTRL_LEFT, Key::CTRL }, // (113) Left Control modifier key.
|
||||
{ AKEYCODE_CTRL_RIGHT, Key::CTRL }, // (114) Right Control modifier key.
|
||||
{ AKEYCODE_CAPS_LOCK, Key::CAPSLOCK }, // (115) Caps Lock key.
|
||||
{ AKEYCODE_SCROLL_LOCK, Key::SCROLLLOCK }, // (116) Scroll Lock key.
|
||||
{ AKEYCODE_META_LEFT, Key::META }, // (117) Left Meta modifier key.
|
||||
{ AKEYCODE_META_RIGHT, Key::META }, // (118) Right Meta modifier key.
|
||||
{ AKEYCODE_SYSRQ, Key::PRINT }, // (120) System Request / Print Screen key.
|
||||
{ AKEYCODE_BREAK, Key::PAUSE }, // (121) Break / Pause key.
|
||||
{ AKEYCODE_MOVE_HOME, Key::HOME }, // (122) Home Movement key.
|
||||
{ AKEYCODE_MOVE_END, Key::END }, // (123) End Movement key.
|
||||
{ AKEYCODE_INSERT, Key::INSERT }, // (124) Insert key.
|
||||
{ AKEYCODE_FORWARD, Key::FORWARD }, // (125) Forward key.
|
||||
{ AKEYCODE_MEDIA_PLAY, Key::MEDIAPLAY }, // (126) Play media key.
|
||||
{ AKEYCODE_MEDIA_RECORD, Key::MEDIARECORD }, // (130) Record media key.
|
||||
{ AKEYCODE_F1, Key::F1 }, // (131) F1 key.
|
||||
{ AKEYCODE_F2, Key::F2 }, // (132) F2 key.
|
||||
{ AKEYCODE_F3, Key::F3 }, // (133) F3 key.
|
||||
{ AKEYCODE_F4, Key::F4 }, // (134) F4 key.
|
||||
{ AKEYCODE_F5, Key::F5 }, // (135) F5 key.
|
||||
{ AKEYCODE_F6, Key::F6 }, // (136) F6 key.
|
||||
{ AKEYCODE_F7, Key::F7 }, // (137) F7 key.
|
||||
{ AKEYCODE_F8, Key::F8 }, // (138) F8 key.
|
||||
{ AKEYCODE_F9, Key::F9 }, // (139) F9 key.
|
||||
{ AKEYCODE_F10, Key::F10 }, // (140) F10 key.
|
||||
{ AKEYCODE_F11, Key::F11 }, // (141) F11 key.
|
||||
{ AKEYCODE_F12, Key::F12 }, // (142) F12 key.
|
||||
{ AKEYCODE_NUM_LOCK, Key::NUMLOCK }, // (143) Num Lock key.
|
||||
{ AKEYCODE_NUMPAD_0, Key::KP_0 }, // (144) Numeric keypad '0' key.
|
||||
{ AKEYCODE_NUMPAD_1, Key::KP_1 }, // (145) Numeric keypad '1' key.
|
||||
{ AKEYCODE_NUMPAD_2, Key::KP_2 }, // (146) Numeric keypad '2' key.
|
||||
{ AKEYCODE_NUMPAD_3, Key::KP_3 }, // (147) Numeric keypad '3' key.
|
||||
{ AKEYCODE_NUMPAD_4, Key::KP_4 }, // (148) Numeric keypad '4' key.
|
||||
{ AKEYCODE_NUMPAD_5, Key::KP_5 }, // (149) Numeric keypad '5' key.
|
||||
{ AKEYCODE_NUMPAD_6, Key::KP_6 }, // (150) Numeric keypad '6' key.
|
||||
{ AKEYCODE_NUMPAD_7, Key::KP_7 }, // (151) Numeric keypad '7' key.
|
||||
{ AKEYCODE_NUMPAD_8, Key::KP_8 }, // (152) Numeric keypad '8' key.
|
||||
{ AKEYCODE_NUMPAD_9, Key::KP_9 }, // (153) Numeric keypad '9' key.
|
||||
{ AKEYCODE_NUMPAD_DIVIDE, Key::KP_DIVIDE }, // (154) Numeric keypad '/' key (for division).
|
||||
{ AKEYCODE_NUMPAD_MULTIPLY, Key::KP_MULTIPLY }, // (155) Numeric keypad '*' key (for multiplication).
|
||||
{ AKEYCODE_NUMPAD_SUBTRACT, Key::KP_SUBTRACT }, // (156) Numeric keypad '-' key (for subtraction).
|
||||
{ AKEYCODE_NUMPAD_ADD, Key::KP_ADD }, // (157) Numeric keypad '+' key (for addition).
|
||||
{ AKEYCODE_NUMPAD_DOT, Key::KP_PERIOD }, // (158) Numeric keypad '.' key (for decimals or digit grouping).
|
||||
{ AKEYCODE_NUMPAD_ENTER, Key::KP_ENTER }, // (160) Numeric keypad Enter key.
|
||||
{ AKEYCODE_VOLUME_MUTE, Key::VOLUMEMUTE }, // (164) Volume Mute key.
|
||||
{ AKEYCODE_EISU, Key::JIS_EISU }, // (212) JIS EISU key.
|
||||
{ AKEYCODE_YEN, Key::YEN }, // (216) Japanese Yen key.
|
||||
{ AKEYCODE_KANA, Key::JIS_KANA }, // (218) JIS KANA key.
|
||||
{ AKEYCODE_HELP, Key::HELP }, // (259) Help key.
|
||||
{ AKEYCODE_REFRESH, Key::REFRESH }, // (285) Refresh key.
|
||||
{ AKEYCODE_MAX, Key::UNKNOWN }
|
||||
};
|
||||
|
||||
Key godot_code_from_android_code(unsigned int p_code);
|
||||
|
||||
// Key location determination.
|
||||
struct AndroidGodotLocationPair {
|
||||
unsigned int android_code = 0;
|
||||
KeyLocation godot_code = KeyLocation::UNSPECIFIED;
|
||||
};
|
||||
|
||||
static AndroidGodotLocationPair android_godot_location_pairs[] = {
|
||||
{ AKEYCODE_ALT_LEFT, KeyLocation::LEFT },
|
||||
{ AKEYCODE_ALT_RIGHT, KeyLocation::RIGHT },
|
||||
{ AKEYCODE_SHIFT_LEFT, KeyLocation::LEFT },
|
||||
{ AKEYCODE_SHIFT_RIGHT, KeyLocation::RIGHT },
|
||||
{ AKEYCODE_CTRL_LEFT, KeyLocation::LEFT },
|
||||
{ AKEYCODE_CTRL_RIGHT, KeyLocation::RIGHT },
|
||||
{ AKEYCODE_META_LEFT, KeyLocation::LEFT },
|
||||
{ AKEYCODE_META_RIGHT, KeyLocation::RIGHT },
|
||||
{ AKEYCODE_MAX, KeyLocation::UNSPECIFIED }
|
||||
};
|
||||
|
||||
KeyLocation godot_location_from_android_code(unsigned int p_code);
|
121
platform/android/api/api.cpp
Normal file
121
platform/android/api/api.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/**************************************************************************/
|
||||
/* api.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "api.h"
|
||||
|
||||
#include "java_class_wrapper.h"
|
||||
#include "jni_singleton.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
static JavaClassWrapper *java_class_wrapper = nullptr;
|
||||
#endif
|
||||
|
||||
void register_android_api() {
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
// On Android platforms, the `java_class_wrapper` instantiation occurs in
|
||||
// `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup`
|
||||
java_class_wrapper = memnew(JavaClassWrapper);
|
||||
#endif
|
||||
GDREGISTER_CLASS(JNISingleton);
|
||||
GDREGISTER_CLASS(JavaClass);
|
||||
GDREGISTER_CLASS(JavaObject);
|
||||
GDREGISTER_CLASS(JavaClassWrapper);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", JavaClassWrapper::get_singleton()));
|
||||
}
|
||||
|
||||
void unregister_android_api() {
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
memdelete(java_class_wrapper);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JavaClass::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_java_class_name"), &JavaClass::get_java_class_name);
|
||||
ClassDB::bind_method(D_METHOD("get_java_method_list"), &JavaClass::get_java_method_list);
|
||||
ClassDB::bind_method(D_METHOD("get_java_parent_class"), &JavaClass::get_java_parent_class);
|
||||
}
|
||||
|
||||
void JavaObject::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_java_class"), &JavaObject::get_java_class);
|
||||
}
|
||||
|
||||
void JavaClassWrapper::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
|
||||
ClassDB::bind_method(D_METHOD("get_exception"), &JavaClassWrapper::get_exception);
|
||||
}
|
||||
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
bool JavaClass::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant JavaClass::callp(const StringName &, const Variant **, int, Callable::CallError &) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
String JavaClass::get_java_class_name() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> JavaClass::get_java_method_list() const {
|
||||
return TypedArray<Dictionary>();
|
||||
}
|
||||
|
||||
Ref<JavaClass> JavaClass::get_java_parent_class() const {
|
||||
return Ref<JavaClass>();
|
||||
}
|
||||
|
||||
JavaClass::JavaClass() {
|
||||
}
|
||||
|
||||
JavaClass::~JavaClass() {
|
||||
}
|
||||
|
||||
Variant JavaObject::callp(const StringName &, const Variant **, int, Callable::CallError &) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
Ref<JavaClass> JavaObject::get_java_class() const {
|
||||
return Ref<JavaClass>();
|
||||
}
|
||||
|
||||
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
||||
|
||||
Ref<JavaClass> JavaClassWrapper::_wrap(const String &, bool) {
|
||||
return Ref<JavaClass>();
|
||||
}
|
||||
|
||||
JavaClassWrapper::JavaClassWrapper() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
#endif
|
34
platform/android/api/api.h
Normal file
34
platform/android/api/api.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**************************************************************************/
|
||||
/* api.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_android_api();
|
||||
void unregister_android_api();
|
297
platform/android/api/java_class_wrapper.h
Normal file
297
platform/android/api/java_class_wrapper.h
Normal file
@@ -0,0 +1,297 @@
|
||||
/**************************************************************************/
|
||||
/* java_class_wrapper.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
class JavaObject;
|
||||
#endif
|
||||
|
||||
class JavaClass : public RefCounted {
|
||||
GDCLASS(JavaClass, RefCounted);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
enum ArgumentType {
|
||||
ARG_TYPE_VOID,
|
||||
ARG_TYPE_BOOLEAN,
|
||||
ARG_TYPE_BYTE,
|
||||
ARG_TYPE_CHAR,
|
||||
ARG_TYPE_SHORT,
|
||||
ARG_TYPE_INT,
|
||||
ARG_TYPE_LONG,
|
||||
ARG_TYPE_FLOAT,
|
||||
ARG_TYPE_DOUBLE,
|
||||
ARG_TYPE_STRING, //special case
|
||||
ARG_TYPE_CHARSEQUENCE,
|
||||
ARG_TYPE_CALLABLE,
|
||||
ARG_TYPE_CLASS,
|
||||
ARG_ARRAY_BIT = 1 << 16,
|
||||
ARG_NUMBER_CLASS_BIT = 1 << 17,
|
||||
ARG_TYPE_MASK = (1 << 16) - 1
|
||||
};
|
||||
|
||||
RBMap<StringName, Variant> constant_map;
|
||||
|
||||
struct MethodInfo {
|
||||
bool _static = false;
|
||||
bool _constructor = false;
|
||||
Vector<uint32_t> param_types;
|
||||
Vector<StringName> param_sigs;
|
||||
uint32_t return_type = 0;
|
||||
jmethodID method;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ static void _convert_to_variant_type(int p_sig, Variant::Type &r_type, float &likelihood) {
|
||||
likelihood = 1.0;
|
||||
r_type = Variant::NIL;
|
||||
|
||||
switch (p_sig) {
|
||||
case ARG_TYPE_VOID:
|
||||
r_type = Variant::NIL;
|
||||
break;
|
||||
case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_BOOLEAN:
|
||||
r_type = Variant::BOOL;
|
||||
break;
|
||||
case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_BYTE:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.1;
|
||||
break;
|
||||
case ARG_TYPE_CHAR | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_CHAR:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.2;
|
||||
break;
|
||||
case ARG_TYPE_SHORT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_SHORT:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.3;
|
||||
break;
|
||||
case ARG_TYPE_INT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_INT:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_LONG:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_TYPE_FLOAT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_FLOAT:
|
||||
r_type = Variant::FLOAT;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_TYPE_DOUBLE | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_DOUBLE:
|
||||
r_type = Variant::FLOAT;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_TYPE_STRING:
|
||||
case ARG_TYPE_CHARSEQUENCE:
|
||||
r_type = Variant::STRING;
|
||||
break;
|
||||
case ARG_TYPE_CALLABLE:
|
||||
r_type = Variant::CALLABLE;
|
||||
break;
|
||||
case ARG_TYPE_CLASS:
|
||||
r_type = Variant::OBJECT;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_VOID:
|
||||
r_type = Variant::NIL;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN:
|
||||
r_type = Variant::ARRAY;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
|
||||
r_type = Variant::PACKED_BYTE_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHAR:
|
||||
r_type = Variant::PACKED_BYTE_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_SHORT:
|
||||
r_type = Variant::PACKED_INT32_ARRAY;
|
||||
likelihood = 0.3;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_INT:
|
||||
r_type = Variant::PACKED_INT32_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_LONG:
|
||||
r_type = Variant::PACKED_INT32_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT:
|
||||
r_type = Variant::PACKED_FLOAT32_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE:
|
||||
r_type = Variant::PACKED_FLOAT32_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE:
|
||||
r_type = Variant::PACKED_STRING_ARRAY;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CLASS:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE:
|
||||
r_type = Variant::ARRAY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static bool _convert_object_to_variant(JNIEnv *env, jobject obj, Variant &var, uint32_t p_sig);
|
||||
|
||||
bool _call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret);
|
||||
|
||||
friend class JavaClassWrapper;
|
||||
friend class JavaObject;
|
||||
String java_class_name;
|
||||
String java_constructor_name;
|
||||
HashMap<StringName, List<MethodInfo>> methods;
|
||||
jclass _class;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
||||
public:
|
||||
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
|
||||
|
||||
String get_java_class_name() const;
|
||||
TypedArray<Dictionary> get_java_method_list() const;
|
||||
Ref<JavaClass> get_java_parent_class() const;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
virtual String to_string() override;
|
||||
#endif
|
||||
|
||||
JavaClass();
|
||||
~JavaClass();
|
||||
};
|
||||
|
||||
class JavaObject : public RefCounted {
|
||||
GDCLASS(JavaObject, RefCounted);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
Ref<JavaClass> base_class;
|
||||
friend class JavaClass;
|
||||
|
||||
jobject instance = nullptr;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
|
||||
|
||||
Ref<JavaClass> get_java_class() const;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
virtual String to_string() override;
|
||||
|
||||
jobject get_instance() { return instance; }
|
||||
|
||||
JavaObject();
|
||||
JavaObject(const Ref<JavaClass> &p_base, jobject p_instance);
|
||||
~JavaObject();
|
||||
#endif
|
||||
};
|
||||
|
||||
class JavaClassWrapper : public Object {
|
||||
GDCLASS(JavaClassWrapper, Object);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
RBMap<String, Ref<JavaClass>> class_cache;
|
||||
friend class JavaClass;
|
||||
jmethodID Class_getDeclaredConstructors;
|
||||
jmethodID Class_getDeclaredMethods;
|
||||
jmethodID Class_getFields;
|
||||
jmethodID Class_getName;
|
||||
jmethodID Class_getSuperclass;
|
||||
jmethodID Constructor_getParameterTypes;
|
||||
jmethodID Constructor_getModifiers;
|
||||
jmethodID Method_getParameterTypes;
|
||||
jmethodID Method_getReturnType;
|
||||
jmethodID Method_getModifiers;
|
||||
jmethodID Method_getName;
|
||||
jmethodID Field_getName;
|
||||
jmethodID Field_getModifiers;
|
||||
jmethodID Field_get;
|
||||
jmethodID Boolean_booleanValue;
|
||||
jmethodID Byte_byteValue;
|
||||
jmethodID Character_characterValue;
|
||||
jmethodID Short_shortValue;
|
||||
jmethodID Integer_integerValue;
|
||||
jmethodID Long_longValue;
|
||||
jmethodID Float_floatValue;
|
||||
jmethodID Double_doubleValue;
|
||||
|
||||
bool _get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig);
|
||||
#endif
|
||||
|
||||
Ref<JavaObject> exception;
|
||||
|
||||
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access);
|
||||
|
||||
static JavaClassWrapper *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static JavaClassWrapper *get_singleton() { return singleton; }
|
||||
|
||||
Ref<JavaClass> wrap(const String &p_class) {
|
||||
return _wrap(p_class, false);
|
||||
}
|
||||
|
||||
Ref<JavaObject> get_exception() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
Ref<JavaClass> wrap_jclass(jclass p_class, bool p_allow_private_methods_access = false);
|
||||
#endif
|
||||
JavaClassWrapper();
|
||||
};
|
104
platform/android/api/jni_singleton.h
Normal file
104
platform/android/api/jni_singleton.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************/
|
||||
/* jni_singleton.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "java_class_wrapper.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class JNISingleton : public Object {
|
||||
GDCLASS(JNISingleton, Object);
|
||||
|
||||
struct MethodData {
|
||||
Variant::Type ret_type;
|
||||
Vector<Variant::Type> argtypes;
|
||||
};
|
||||
|
||||
RBMap<StringName, MethodData> method_map;
|
||||
Ref<JavaObject> wrapped_object;
|
||||
|
||||
public:
|
||||
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||
if (wrapped_object.is_valid()) {
|
||||
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
|
||||
|
||||
// Check the method we're looking for is in the JNISingleton map and that
|
||||
// the arguments match.
|
||||
bool call_error = !E || E->get().argtypes.size() != p_argcount;
|
||||
if (!call_error) {
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
|
||||
call_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!call_error) {
|
||||
return wrapped_object->callp(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
}
|
||||
|
||||
return Object::callp(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
Ref<JavaObject> get_wrapped_object() const {
|
||||
return wrapped_object;
|
||||
}
|
||||
|
||||
void add_method(const StringName &p_name, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
|
||||
MethodData md;
|
||||
md.argtypes = p_args;
|
||||
md.ret_type = p_ret_type;
|
||||
method_map[p_name] = md;
|
||||
}
|
||||
|
||||
void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
|
||||
MethodInfo mi;
|
||||
mi.name = p_name;
|
||||
for (int i = 0; i < p_args.size(); i++) {
|
||||
mi.arguments.push_back(PropertyInfo(p_args[i], "arg" + itos(i + 1)));
|
||||
}
|
||||
ADD_SIGNAL(mi);
|
||||
}
|
||||
|
||||
JNISingleton() {}
|
||||
|
||||
JNISingleton(const Ref<JavaObject> &p_wrapped_object) {
|
||||
wrapped_object = p_wrapped_object;
|
||||
}
|
||||
|
||||
~JNISingleton() {
|
||||
method_map.clear();
|
||||
wrapped_object.unref();
|
||||
}
|
||||
};
|
361
platform/android/audio_driver_opensl.cpp
Normal file
361
platform/android/audio_driver_opensl.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
/**************************************************************************/
|
||||
/* audio_driver_opensl.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "audio_driver_opensl.h"
|
||||
|
||||
#define MAX_NUMBER_INTERFACES 3
|
||||
#define MAX_NUMBER_OUTPUT_DEVICES 6
|
||||
|
||||
/* Structure for passing information to callback function */
|
||||
|
||||
void AudioDriverOpenSL::_buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf) {
|
||||
bool mix = true;
|
||||
|
||||
if (pause) {
|
||||
mix = false;
|
||||
} else {
|
||||
mix = mutex.try_lock();
|
||||
}
|
||||
|
||||
if (mix) {
|
||||
audio_server_process(buffer_size, mixdown_buffer);
|
||||
} else {
|
||||
int32_t *src_buff = mixdown_buffer;
|
||||
for (unsigned int i = 0; i < buffer_size * 2; i++) {
|
||||
src_buff[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mix) {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
const int32_t *src_buff = mixdown_buffer;
|
||||
|
||||
int16_t *ptr = (int16_t *)buffers[last_free];
|
||||
last_free = (last_free + 1) % BUFFER_COUNT;
|
||||
|
||||
for (unsigned int i = 0; i < buffer_size * 2; i++) {
|
||||
ptr[i] = src_buff[i] >> 16;
|
||||
}
|
||||
|
||||
(*queueItf)->Enqueue(queueItf, ptr, 4 * buffer_size);
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext) {
|
||||
AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
|
||||
|
||||
ad->_buffer_callback(queueItf);
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::init() {
|
||||
SLresult res;
|
||||
SLEngineOption EngineOption[] = {
|
||||
{ (SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE }
|
||||
};
|
||||
res = slCreateEngine(&sl, 1, EngineOption, 0, nullptr, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(res != SL_RESULT_SUCCESS, ERR_INVALID_PARAMETER, "Could not initialize OpenSL.");
|
||||
|
||||
res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND_V_MSG(res != SL_RESULT_SUCCESS, ERR_INVALID_PARAMETER, "Could not realize OpenSL.");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::start() {
|
||||
active = false;
|
||||
|
||||
SLresult res;
|
||||
|
||||
buffer_size = 1024;
|
||||
|
||||
for (int i = 0; i < BUFFER_COUNT; i++) {
|
||||
buffers[i] = memnew_arr(int16_t, buffer_size * 2);
|
||||
memset(buffers[i], 0, buffer_size * 4);
|
||||
}
|
||||
|
||||
mixdown_buffer = memnew_arr(int32_t, buffer_size * 2);
|
||||
|
||||
/* Callback context for the buffer queue callback function */
|
||||
|
||||
/* Get the SL Engine Interface which is implicit */
|
||||
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf);
|
||||
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
{
|
||||
const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
|
||||
res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, ids, req);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
// Realizing the Output Mix object in synchronous mode.
|
||||
res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, BUFFER_COUNT };
|
||||
/* Setup the format of the content in the buffer queue */
|
||||
pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
pcm.numChannels = 2;
|
||||
pcm.samplesPerSec = SL_SAMPLINGRATE_44_1;
|
||||
pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
#ifdef BIG_ENDIAN_ENABLED
|
||||
pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
#else
|
||||
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
#endif
|
||||
audioSource.pFormat = (void *)&pcm;
|
||||
audioSource.pLocator = (void *)&loc_bufq;
|
||||
|
||||
/* Setup the data sink structure */
|
||||
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
locator_outputmix.outputMix = OutputMix;
|
||||
audioSink.pLocator = (void *)&locator_outputmix;
|
||||
audioSink.pFormat = nullptr;
|
||||
|
||||
/* Create the music player */
|
||||
{
|
||||
const SLInterfaceID ids[2] = { SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
|
||||
res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 1, ids, req);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
}
|
||||
/* Realizing the player in synchronous mode. */
|
||||
res = (*player)->Realize(player, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
/* Get seek and play interfaces */
|
||||
res = (*player)->GetInterface(player, SL_IID_PLAY, (void *)&playItf);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE,
|
||||
(void *)&bufferQueueItf);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
/* Setup to receive buffer queue event callbacks */
|
||||
res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf, _buffer_callbacks, this);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
last_free = 0;
|
||||
|
||||
//fill up buffers
|
||||
for (int i = 0; i < BUFFER_COUNT; i++) {
|
||||
/* Enqueue a few buffers to get the ball rolling */
|
||||
res = (*bufferQueueItf)->Enqueue(bufferQueueItf, buffers[i], 4 * buffer_size); /* Size given in */
|
||||
}
|
||||
|
||||
res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
active = true;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf queueItf) {
|
||||
for (int i = 0; i < rec_buffer.size(); i++) {
|
||||
int32_t sample = rec_buffer[i] << 16;
|
||||
input_buffer_write(sample);
|
||||
input_buffer_write(sample); // call twice to convert to Stereo
|
||||
}
|
||||
|
||||
SLresult res = (*recordBufferQueueItf)->Enqueue(recordBufferQueueItf, rec_buffer.ptrw(), rec_buffer.size() * sizeof(int16_t));
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {
|
||||
AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
|
||||
|
||||
ad->_record_buffer_callback(queueItf);
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::init_input_device() {
|
||||
SLDataLocator_IODevice loc_dev = {
|
||||
SL_DATALOCATOR_IODEVICE,
|
||||
SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT,
|
||||
nullptr
|
||||
};
|
||||
SLDataSource recSource = { &loc_dev, nullptr };
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {
|
||||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
2
|
||||
};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
1,
|
||||
SL_SAMPLINGRATE_44_1,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_CENTER,
|
||||
SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
SLDataSink recSnk = { &loc_bq, &format_pcm };
|
||||
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
|
||||
SLresult res = (*EngineItf)->CreateAudioRecorder(EngineItf, &recorder, &recSource, &recSnk, 2, ids, req);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->Realize(recorder, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->GetInterface(recorder, SL_IID_RECORD, (void *)&recordItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->GetInterface(recorder, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void *)&recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->RegisterCallback(recordBufferQueueItf, _record_buffer_callbacks, this);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
SLuint32 state;
|
||||
res = (*recordItf)->GetRecordState(recordItf, &state);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
if (state != SL_RECORDSTATE_STOPPED) {
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->Clear(recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
const int rec_buffer_frames = 2048;
|
||||
rec_buffer.resize(rec_buffer_frames);
|
||||
input_buffer_init(rec_buffer_frames);
|
||||
|
||||
res = (*recordBufferQueueItf)->Enqueue(recordBufferQueueItf, rec_buffer.ptrw(), rec_buffer.size() * sizeof(int16_t));
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::input_start() {
|
||||
if (recordItf || recordBufferQueueItf) {
|
||||
return ERR_ALREADY_IN_USE;
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->request_permission("RECORD_AUDIO")) {
|
||||
return init_input_device();
|
||||
}
|
||||
|
||||
WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
|
||||
return ERR_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::input_stop() {
|
||||
if (!recordItf || !recordBufferQueueItf) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
SLuint32 state;
|
||||
SLresult res = (*recordItf)->GetRecordState(recordItf, &state);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
if (state != SL_RECORDSTATE_STOPPED) {
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->Clear(recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int AudioDriverOpenSL::get_mix_rate() const {
|
||||
return 44100; // hardcoded for Android, as selected by SL_SAMPLINGRATE_44_1
|
||||
}
|
||||
|
||||
AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const {
|
||||
return SPEAKER_MODE_STEREO;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::lock() {
|
||||
if (active) {
|
||||
mutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::unlock() {
|
||||
if (active) {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::finish() {
|
||||
if (recordItf) {
|
||||
(*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
|
||||
recordItf = nullptr;
|
||||
}
|
||||
if (recorder) {
|
||||
(*recorder)->Destroy(recorder);
|
||||
recorder = nullptr;
|
||||
}
|
||||
if (playItf) {
|
||||
(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
|
||||
playItf = nullptr;
|
||||
}
|
||||
if (player) {
|
||||
(*player)->Destroy(player);
|
||||
player = nullptr;
|
||||
}
|
||||
if (OutputMix) {
|
||||
(*OutputMix)->Destroy(OutputMix);
|
||||
OutputMix = nullptr;
|
||||
}
|
||||
if (sl) {
|
||||
(*sl)->Destroy(sl);
|
||||
sl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::set_pause(bool p_pause) {
|
||||
pause = p_pause;
|
||||
|
||||
if (active && playItf) {
|
||||
if (pause) {
|
||||
(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
|
||||
} else {
|
||||
(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioDriverOpenSL::AudioDriverOpenSL() {
|
||||
}
|
108
platform/android/audio_driver_opensl.h
Normal file
108
platform/android/audio_driver_opensl.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/**************************************************************************/
|
||||
/* audio_driver_opensl.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
class AudioDriverOpenSL : public AudioDriver {
|
||||
bool active = false;
|
||||
Mutex mutex;
|
||||
|
||||
enum {
|
||||
BUFFER_COUNT = 2
|
||||
};
|
||||
|
||||
bool pause = false;
|
||||
|
||||
uint32_t buffer_size = 0;
|
||||
int16_t *buffers[BUFFER_COUNT] = {};
|
||||
int32_t *mixdown_buffer = nullptr;
|
||||
int last_free = 0;
|
||||
|
||||
Vector<int16_t> rec_buffer;
|
||||
|
||||
SLPlayItf playItf = nullptr;
|
||||
SLRecordItf recordItf = nullptr;
|
||||
SLObjectItf sl = nullptr;
|
||||
SLEngineItf EngineItf = nullptr;
|
||||
SLObjectItf OutputMix = nullptr;
|
||||
SLObjectItf player = nullptr;
|
||||
SLObjectItf recorder = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueueItf = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf recordBufferQueueItf = nullptr;
|
||||
SLDataSource audioSource;
|
||||
SLDataFormat_PCM pcm;
|
||||
SLDataSink audioSink;
|
||||
SLDataLocator_OutputMix locator_outputmix;
|
||||
|
||||
static AudioDriverOpenSL *s_ad;
|
||||
|
||||
void _buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf);
|
||||
|
||||
static void _buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext);
|
||||
|
||||
void _record_buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf);
|
||||
|
||||
static void _record_buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext);
|
||||
|
||||
Error init_input_device();
|
||||
|
||||
public:
|
||||
virtual const char *get_name() const override {
|
||||
return "Android";
|
||||
}
|
||||
|
||||
virtual Error init() override;
|
||||
virtual void start() override;
|
||||
virtual int get_mix_rate() const override;
|
||||
virtual SpeakerMode get_speaker_mode() const override;
|
||||
|
||||
virtual void lock() override;
|
||||
virtual void unlock() override;
|
||||
virtual void finish() override;
|
||||
|
||||
virtual Error input_start() override;
|
||||
virtual Error input_stop() override;
|
||||
|
||||
void set_pause(bool p_pause);
|
||||
|
||||
AudioDriverOpenSL();
|
||||
};
|
248
platform/android/detect.py
Normal file
248
platform/android/detect.py
Normal file
@@ -0,0 +1,248 @@
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from methods import print_error, print_warning
|
||||
from platform_methods import validate_arch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from SCons.Script.SConscript import SConsEnvironment
|
||||
|
||||
|
||||
def get_name():
|
||||
return "Android"
|
||||
|
||||
|
||||
def can_build():
|
||||
return os.path.exists(get_env_android_sdk_root())
|
||||
|
||||
|
||||
def get_tools(env: "SConsEnvironment"):
|
||||
return ["clang", "clang++", "as", "ar", "link"]
|
||||
|
||||
|
||||
def get_opts():
|
||||
from SCons.Variables import BoolVariable
|
||||
|
||||
return [
|
||||
("ANDROID_HOME", "Path to the Android SDK", get_env_android_sdk_root()),
|
||||
(
|
||||
"ndk_platform",
|
||||
'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")',
|
||||
"android-" + str(get_min_target_api()),
|
||||
),
|
||||
BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False),
|
||||
BoolVariable(
|
||||
("generate_android_binaries", "generate_apk"),
|
||||
"Generate APK, AAB & AAR binaries after building Android library by calling Gradle",
|
||||
False,
|
||||
),
|
||||
BoolVariable("swappy", "Use Swappy Frame Pacing library", False),
|
||||
]
|
||||
|
||||
|
||||
def get_doc_classes():
|
||||
return [
|
||||
"EditorExportPlatformAndroid",
|
||||
]
|
||||
|
||||
|
||||
def get_doc_path():
|
||||
return "doc_classes"
|
||||
|
||||
|
||||
# Return the ANDROID_HOME environment variable.
|
||||
def get_env_android_sdk_root():
|
||||
return os.environ.get("ANDROID_HOME", os.environ.get("ANDROID_SDK_ROOT", ""))
|
||||
|
||||
|
||||
def get_min_sdk_version(platform):
|
||||
return int(platform.split("-")[1])
|
||||
|
||||
|
||||
def get_android_ndk_root(env: "SConsEnvironment"):
|
||||
return os.path.join(env["ANDROID_HOME"], "ndk", get_ndk_version())
|
||||
|
||||
|
||||
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
|
||||
def get_ndk_version():
|
||||
return "28.1.13356709"
|
||||
|
||||
|
||||
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
|
||||
def get_min_target_api():
|
||||
return 24
|
||||
|
||||
|
||||
def get_flags():
|
||||
return {
|
||||
"arch": "arm64",
|
||||
"target": "template_debug",
|
||||
"supported": ["mono"],
|
||||
}
|
||||
|
||||
|
||||
# Check if Android NDK version is installed
|
||||
# If not, install it.
|
||||
def install_ndk_if_needed(env: "SConsEnvironment"):
|
||||
sdk_root = env["ANDROID_HOME"]
|
||||
if not os.path.exists(get_android_ndk_root(env)):
|
||||
extension = ".bat" if os.name == "nt" else ""
|
||||
sdkmanager = os.path.join(sdk_root, "cmdline-tools", "latest", "bin", "sdkmanager" + extension)
|
||||
if os.path.exists(sdkmanager):
|
||||
# Install the Android NDK
|
||||
print("Installing Android NDK...")
|
||||
ndk_download_args = "ndk;" + get_ndk_version()
|
||||
subprocess.check_call([sdkmanager, ndk_download_args])
|
||||
else:
|
||||
print_error(
|
||||
f'Cannot find "{sdkmanager}". Please ensure ANDROID_HOME is correct and cmdline-tools'
|
||||
f' are installed, or install NDK version "{get_ndk_version()}" manually.'
|
||||
)
|
||||
sys.exit(255)
|
||||
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
|
||||
|
||||
|
||||
def detect_swappy():
|
||||
archs = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]
|
||||
has_swappy = True
|
||||
for arch in archs:
|
||||
if not os.path.isfile(f"thirdparty/swappy-frame-pacing/{arch}/libswappy_static.a"):
|
||||
has_swappy = False
|
||||
return has_swappy
|
||||
|
||||
|
||||
def configure(env: "SConsEnvironment"):
|
||||
# Validate arch.
|
||||
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
|
||||
validate_arch(env["arch"], get_name(), supported_arches)
|
||||
|
||||
if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api():
|
||||
print_warning(
|
||||
"Minimum supported Android target api is %d. Forcing target api %d."
|
||||
% (get_min_target_api(), get_min_target_api())
|
||||
)
|
||||
env["ndk_platform"] = "android-" + str(get_min_target_api())
|
||||
|
||||
install_ndk_if_needed(env)
|
||||
ndk_root = env["ANDROID_NDK_ROOT"]
|
||||
|
||||
# Architecture
|
||||
|
||||
if env["arch"] == "arm32":
|
||||
target_triple = "armv7a-linux-androideabi"
|
||||
elif env["arch"] == "arm64":
|
||||
target_triple = "aarch64-linux-android"
|
||||
elif env["arch"] == "x86_32":
|
||||
target_triple = "i686-linux-android"
|
||||
elif env["arch"] == "x86_64":
|
||||
target_triple = "x86_64-linux-android"
|
||||
|
||||
target_option = ["-target", target_triple + str(get_min_sdk_version(env["ndk_platform"]))]
|
||||
env.Append(ASFLAGS=[target_option, "-c"])
|
||||
env.Append(CCFLAGS=target_option)
|
||||
env.Append(LINKFLAGS=target_option)
|
||||
|
||||
# LTO
|
||||
|
||||
if env["lto"] == "auto": # LTO benefits for Android (size, performance) haven't been clearly established yet.
|
||||
env["lto"] = "none"
|
||||
|
||||
if env["lto"] != "none":
|
||||
if env["lto"] == "thin":
|
||||
env.Append(CCFLAGS=["-flto=thin"])
|
||||
env.Append(LINKFLAGS=["-flto=thin"])
|
||||
else:
|
||||
env.Append(CCFLAGS=["-flto"])
|
||||
env.Append(LINKFLAGS=["-flto"])
|
||||
|
||||
# Compiler configuration
|
||||
|
||||
env["SHLIBSUFFIX"] = ".so"
|
||||
|
||||
if env["PLATFORM"] == "win32":
|
||||
env.use_windows_spawn_fix()
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
host_subpath = "linux-x86_64"
|
||||
elif sys.platform.startswith("darwin"):
|
||||
host_subpath = "darwin-x86_64"
|
||||
elif sys.platform.startswith("win"):
|
||||
if platform.machine().endswith("64"):
|
||||
host_subpath = "windows-x86_64"
|
||||
else:
|
||||
host_subpath = "windows"
|
||||
|
||||
toolchain_path = os.path.join(ndk_root, "toolchains", "llvm", "prebuilt", host_subpath)
|
||||
compiler_path = os.path.join(toolchain_path, "bin")
|
||||
|
||||
env["CC"] = os.path.join(compiler_path, "clang")
|
||||
env["CXX"] = os.path.join(compiler_path, "clang++")
|
||||
env["AR"] = os.path.join(compiler_path, "llvm-ar")
|
||||
env["RANLIB"] = os.path.join(compiler_path, "llvm-ranlib")
|
||||
env["AS"] = os.path.join(compiler_path, "clang")
|
||||
|
||||
env.Append(
|
||||
CCFLAGS=(["-fpic", "-ffunction-sections", "-funwind-tables", "-fstack-protector-strong", "-fvisibility=hidden"])
|
||||
)
|
||||
|
||||
has_swappy = detect_swappy()
|
||||
if not has_swappy:
|
||||
print_warning(
|
||||
"Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/godotengine/godot-swappy/releases and extract it so that the following files can be found:\n"
|
||||
+ " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/x86/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/x86_64/libswappy_static.a\n"
|
||||
+ "Without Swappy, Godot apps on Android will inevitable suffer stutter and struggle to keep consistent 30/60/90/120 fps. Though Swappy cannot guarantee your app will be stutter-free, not having Swappy will guarantee there will be stutter even on the best phones and the most simple of scenes."
|
||||
)
|
||||
if env["swappy"]:
|
||||
print_error("Use build option `swappy=no` to ignore missing Swappy dependency and build without it.")
|
||||
sys.exit(255)
|
||||
|
||||
if get_min_sdk_version(env["ndk_platform"]) >= 24:
|
||||
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
|
||||
|
||||
if env["arch"] == "x86_32":
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["#thirdparty/swappy-frame-pacing/x86"])
|
||||
elif env["arch"] == "x86_64":
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["#thirdparty/swappy-frame-pacing/x86_64"])
|
||||
elif env["arch"] == "arm32":
|
||||
env.Append(CCFLAGS=["-march=armv7-a", "-mfloat-abi=softfp"])
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
|
||||
env.Append(CPPDEFINES=["__ARM_NEON__"])
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["#thirdparty/swappy-frame-pacing/armeabi-v7a"])
|
||||
elif env["arch"] == "arm64":
|
||||
env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["#thirdparty/swappy-frame-pacing/arm64-v8a"])
|
||||
|
||||
env.Append(CCFLAGS=["-ffp-contract=off"])
|
||||
|
||||
# Link flags
|
||||
|
||||
env.Append(LINKFLAGS=["-Wl,--gc-sections", "-Wl,--no-undefined", "-Wl,-z,now"])
|
||||
env.Append(LINKFLAGS=["-Wl,--build-id"])
|
||||
env.Append(LINKFLAGS=["-Wl,-soname,libgodot_android.so"])
|
||||
|
||||
env.Prepend(CPPPATH=["#platform/android"])
|
||||
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
|
||||
env.Append(LIBS=["OpenSLES", "EGL", "android", "log", "z", "dl"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
if has_swappy:
|
||||
env.Append(CPPDEFINES=["SWAPPY_FRAME_PACING_ENABLED"])
|
||||
env.Append(LIBS=["swappy_static"])
|
||||
if not env["use_volk"]:
|
||||
env.Append(LIBS=["vulkan"])
|
||||
|
||||
if env["opengl3"]:
|
||||
env.Append(CPPDEFINES=["GLES3_ENABLED"])
|
||||
env.Append(LIBS=["GLESv3"])
|
52
platform/android/dialog_utils_jni.cpp
Normal file
52
platform/android/dialog_utils_jni.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************/
|
||||
/* dialog_utils_jni.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "dialog_utils_jni.h"
|
||||
|
||||
#include "display_server_android.h"
|
||||
#include "jni_utils.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index) {
|
||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
ds->emit_dialog_callback(p_button_index);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) {
|
||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
String text = jstring_to_string(p_text, env);
|
||||
ds->emit_input_dialog_callback(text);
|
||||
}
|
||||
}
|
||||
}
|
38
platform/android/dialog_utils_jni.h
Normal file
38
platform/android/dialog_utils_jni.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/**************************************************************************/
|
||||
/* dialog_utils_jni.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text);
|
||||
}
|
364
platform/android/dir_access_jandroid.cpp
Normal file
364
platform/android/dir_access_jandroid.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/**************************************************************************/
|
||||
/* dir_access_jandroid.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "dir_access_jandroid.h"
|
||||
|
||||
#include "jni_utils.h"
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
jobject DirAccessJAndroid::dir_access_handler = nullptr;
|
||||
jclass DirAccessJAndroid::cls = nullptr;
|
||||
jmethodID DirAccessJAndroid::_dir_open = nullptr;
|
||||
jmethodID DirAccessJAndroid::_dir_next = nullptr;
|
||||
jmethodID DirAccessJAndroid::_dir_close = nullptr;
|
||||
jmethodID DirAccessJAndroid::_dir_is_dir = nullptr;
|
||||
jmethodID DirAccessJAndroid::_dir_exists = nullptr;
|
||||
jmethodID DirAccessJAndroid::_file_exists = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_drive_count = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_drive = nullptr;
|
||||
jmethodID DirAccessJAndroid::_make_dir = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_space_left = nullptr;
|
||||
jmethodID DirAccessJAndroid::_rename = nullptr;
|
||||
jmethodID DirAccessJAndroid::_remove = nullptr;
|
||||
jmethodID DirAccessJAndroid::_current_is_hidden = nullptr;
|
||||
|
||||
Error DirAccessJAndroid::list_dir_begin() {
|
||||
list_dir_end();
|
||||
int res = dir_open(current_dir);
|
||||
if (res <= 0) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
id = res;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_next() {
|
||||
ERR_FAIL_COND_V(id == 0, "");
|
||||
if (_dir_next) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, "");
|
||||
jstring str = (jstring)env->CallObjectMethod(dir_access_handler, _dir_next, id);
|
||||
if (!str) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String ret = jstring_to_string((jstring)str, env);
|
||||
env->DeleteLocalRef((jobject)str);
|
||||
return ret;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::current_is_dir() const {
|
||||
if (_dir_is_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
return env->CallBooleanMethod(dir_access_handler, _dir_is_dir, id);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::current_is_hidden() const {
|
||||
if (_current_is_hidden) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
return env->CallBooleanMethod(dir_access_handler, _current_is_hidden, id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::list_dir_end() {
|
||||
if (id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dir_close(id);
|
||||
id = 0;
|
||||
}
|
||||
|
||||
int DirAccessJAndroid::get_drive_count() {
|
||||
if (_get_drive_count) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
return env->CallIntMethod(dir_access_handler, _get_drive_count, get_access_type());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_drive(int p_drive) {
|
||||
if (_get_drive) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, "");
|
||||
jstring j_drive = (jstring)env->CallObjectMethod(dir_access_handler, _get_drive, get_access_type(), p_drive);
|
||||
if (!j_drive) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String drive = jstring_to_string(j_drive, env);
|
||||
env->DeleteLocalRef(j_drive);
|
||||
return drive;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::_get_root_string() const {
|
||||
if (get_access_type() == ACCESS_FILESYSTEM) {
|
||||
return "/";
|
||||
}
|
||||
return DirAccessUnix::_get_root_string();
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_current_dir(bool p_include_drive) const {
|
||||
String base = _get_root_path();
|
||||
String bd = current_dir;
|
||||
if (!base.is_empty()) {
|
||||
bd = current_dir.replace_first(base, "");
|
||||
}
|
||||
|
||||
String root_string = _get_root_string();
|
||||
if (bd.begins_with(root_string)) {
|
||||
return bd;
|
||||
} else if (bd.begins_with("/")) {
|
||||
return root_string + bd.substr(1);
|
||||
} else {
|
||||
return root_string + bd;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::change_dir(String p_dir) {
|
||||
String new_dir = get_absolute_path(p_dir);
|
||||
if (new_dir == current_dir) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (!dir_exists(new_dir)) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
current_dir = new_dir;
|
||||
return OK;
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_absolute_path(String p_path) {
|
||||
if (current_dir != "" && p_path == current_dir) {
|
||||
return current_dir;
|
||||
}
|
||||
|
||||
if (p_path.is_relative_path()) {
|
||||
p_path = get_current_dir().path_join(p_path);
|
||||
}
|
||||
|
||||
p_path = fix_path(p_path);
|
||||
p_path = p_path.simplify_path();
|
||||
return p_path;
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::file_exists(String p_file) {
|
||||
if (_file_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
|
||||
String path = get_absolute_path(p_file);
|
||||
jstring j_path = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _file_exists, get_access_type(), j_path);
|
||||
env->DeleteLocalRef(j_path);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::dir_exists(String p_dir) {
|
||||
if (_dir_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
|
||||
String path = get_absolute_path(p_dir);
|
||||
jstring j_path = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _dir_exists, get_access_type(), j_path);
|
||||
env->DeleteLocalRef(j_path);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::make_dir(String p_dir) {
|
||||
// Check if the directory exists already
|
||||
if (dir_exists(p_dir)) {
|
||||
return ERR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
if (_make_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||
|
||||
String path = get_absolute_path(p_dir);
|
||||
jstring j_dir = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _make_dir, get_access_type(), j_dir);
|
||||
env->DeleteLocalRef(j_dir);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::make_dir_recursive(const String &p_dir) {
|
||||
Error err = make_dir(p_dir);
|
||||
if (err != OK && err != ERR_ALREADY_EXISTS) {
|
||||
ERR_FAIL_V_MSG(err, "Could not create directory: " + p_dir);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::rename(String p_from, String p_to) {
|
||||
if (_rename) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||
|
||||
String from_path = get_absolute_path(p_from);
|
||||
jstring j_from = env->NewStringUTF(from_path.utf8().get_data());
|
||||
|
||||
String to_path = get_absolute_path(p_to);
|
||||
jstring j_to = env->NewStringUTF(to_path.utf8().get_data());
|
||||
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _rename, get_access_type(), j_from, j_to);
|
||||
env->DeleteLocalRef(j_from);
|
||||
env->DeleteLocalRef(j_to);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::remove(String p_name) {
|
||||
if (_remove) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||
|
||||
String path = get_absolute_path(p_name);
|
||||
jstring j_name = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _remove, get_access_type(), j_name);
|
||||
env->DeleteLocalRef(j_name);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DirAccessJAndroid::get_space_left() {
|
||||
if (_get_space_left) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
return env->CallLongMethod(dir_access_handler, _get_space_left, get_access_type());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::setup(jobject p_dir_access_handler) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
dir_access_handler = env->NewGlobalRef(p_dir_access_handler);
|
||||
|
||||
jclass c = env->GetObjectClass(dir_access_handler);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_dir_open = env->GetMethodID(cls, "dirOpen", "(ILjava/lang/String;)I");
|
||||
_dir_next = env->GetMethodID(cls, "dirNext", "(I)Ljava/lang/String;");
|
||||
_dir_close = env->GetMethodID(cls, "dirClose", "(I)V");
|
||||
_dir_is_dir = env->GetMethodID(cls, "dirIsDir", "(I)Z");
|
||||
_dir_exists = env->GetMethodID(cls, "dirExists", "(ILjava/lang/String;)Z");
|
||||
_file_exists = env->GetMethodID(cls, "fileExists", "(ILjava/lang/String;)Z");
|
||||
_get_drive_count = env->GetMethodID(cls, "getDriveCount", "(I)I");
|
||||
_get_drive = env->GetMethodID(cls, "getDrive", "(II)Ljava/lang/String;");
|
||||
_make_dir = env->GetMethodID(cls, "makeDir", "(ILjava/lang/String;)Z");
|
||||
_get_space_left = env->GetMethodID(cls, "getSpaceLeft", "(I)J");
|
||||
_rename = env->GetMethodID(cls, "rename", "(ILjava/lang/String;Ljava/lang/String;)Z");
|
||||
_remove = env->GetMethodID(cls, "remove", "(ILjava/lang/String;)Z");
|
||||
_current_is_hidden = env->GetMethodID(cls, "isCurrentHidden", "(I)Z");
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::terminate() {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
env->DeleteGlobalRef(cls);
|
||||
env->DeleteGlobalRef(dir_access_handler);
|
||||
}
|
||||
|
||||
DirAccessJAndroid::DirAccessJAndroid() {
|
||||
}
|
||||
|
||||
DirAccessJAndroid::~DirAccessJAndroid() {
|
||||
list_dir_end();
|
||||
}
|
||||
|
||||
int DirAccessJAndroid::dir_open(String p_path) {
|
||||
if (_dir_open) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
String path = get_absolute_path(p_path);
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int dirId = env->CallIntMethod(dir_access_handler, _dir_open, get_access_type(), js);
|
||||
env->DeleteLocalRef(js);
|
||||
return dirId;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::dir_close(int p_id) {
|
||||
if (_dir_close) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
env->CallVoidMethod(dir_access_handler, _dir_close, p_id);
|
||||
}
|
||||
}
|
106
platform/android/dir_access_jandroid.h
Normal file
106
platform/android/dir_access_jandroid.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/**************************************************************************/
|
||||
/* dir_access_jandroid.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "java_godot_lib_jni.h"
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "drivers/unix/dir_access_unix.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
/// Android implementation of the DirAccess interface used to provide access to
|
||||
/// ACCESS_FILESYSTEM and ACCESS_RESOURCES directory resources.
|
||||
/// The implementation use jni in order to comply with Android filesystem
|
||||
/// access restriction.
|
||||
class DirAccessJAndroid : public DirAccessUnix {
|
||||
GDSOFTCLASS(DirAccessJAndroid, DirAccessUnix);
|
||||
static jobject dir_access_handler;
|
||||
static jclass cls;
|
||||
|
||||
static jmethodID _dir_open;
|
||||
static jmethodID _dir_next;
|
||||
static jmethodID _dir_close;
|
||||
static jmethodID _dir_is_dir;
|
||||
static jmethodID _dir_exists;
|
||||
static jmethodID _file_exists;
|
||||
static jmethodID _get_drive_count;
|
||||
static jmethodID _get_drive;
|
||||
static jmethodID _make_dir;
|
||||
static jmethodID _get_space_left;
|
||||
static jmethodID _rename;
|
||||
static jmethodID _remove;
|
||||
static jmethodID _current_is_hidden;
|
||||
|
||||
public:
|
||||
virtual Error list_dir_begin() override; ///< This starts dir listing
|
||||
virtual String get_next() override;
|
||||
virtual bool current_is_dir() const override;
|
||||
virtual bool current_is_hidden() const override;
|
||||
virtual void list_dir_end() override; ///<
|
||||
|
||||
virtual int get_drive_count() override;
|
||||
virtual String get_drive(int p_drive) override;
|
||||
virtual String get_current_dir(bool p_include_drive = true) const override; ///< return current dir location
|
||||
|
||||
virtual Error change_dir(String p_dir) override; ///< can be relative or absolute, return false on success
|
||||
|
||||
virtual bool file_exists(String p_file) override;
|
||||
virtual bool dir_exists(String p_dir) override;
|
||||
|
||||
virtual Error make_dir(String p_dir) override;
|
||||
virtual Error make_dir_recursive(const String &p_dir) override;
|
||||
|
||||
virtual Error rename(String p_from, String p_to) override;
|
||||
virtual Error remove(String p_name) override;
|
||||
|
||||
virtual bool is_link(String p_file) override { return false; }
|
||||
virtual String read_link(String p_file) override { return p_file; }
|
||||
virtual Error create_link(String p_source, String p_target) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual uint64_t get_space_left() override;
|
||||
|
||||
static void setup(jobject p_dir_access_handler);
|
||||
static void terminate();
|
||||
|
||||
DirAccessJAndroid();
|
||||
~DirAccessJAndroid();
|
||||
|
||||
protected:
|
||||
String _get_root_string() const override;
|
||||
|
||||
private:
|
||||
int id = 0;
|
||||
|
||||
int dir_open(String p_path);
|
||||
void dir_close(int p_id);
|
||||
String get_absolute_path(String p_path);
|
||||
};
|
978
platform/android/display_server_android.cpp
Normal file
978
platform/android/display_server_android.cpp
Normal file
@@ -0,0 +1,978 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_android.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "display_server_android.h"
|
||||
|
||||
#include "java_godot_io_wrapper.h"
|
||||
#include "java_godot_wrapper.h"
|
||||
#include "os_android.h"
|
||||
#include "tts_android.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#include "rendering_context_driver_vulkan_android.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#endif
|
||||
|
||||
DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
|
||||
return static_cast<DisplayServerAndroid *>(DisplayServer::get_singleton());
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::has_feature(Feature p_feature) const {
|
||||
switch (p_feature) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
case FEATURE_GLOBAL_MENU: {
|
||||
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
|
||||
} break;
|
||||
#endif
|
||||
case FEATURE_NATIVE_DIALOG_FILE: {
|
||||
String sdk_version = OS::get_singleton()->get_version().get_slicec('.', 0);
|
||||
return sdk_version.to_int() >= 29;
|
||||
} break;
|
||||
|
||||
case FEATURE_CURSOR_SHAPE:
|
||||
//case FEATURE_CUSTOM_CURSOR_SHAPE:
|
||||
//case FEATURE_HIDPI:
|
||||
//case FEATURE_ICON:
|
||||
//case FEATURE_IME:
|
||||
case FEATURE_MOUSE:
|
||||
//case FEATURE_MOUSE_WARP:
|
||||
case FEATURE_NATIVE_DIALOG:
|
||||
case FEATURE_NATIVE_DIALOG_INPUT:
|
||||
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||
case FEATURE_NATIVE_DIALOG_FILE_MIME:
|
||||
//case FEATURE_NATIVE_ICON:
|
||||
case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_CLIPBOARD:
|
||||
case FEATURE_KEEP_SCREEN_ON:
|
||||
case FEATURE_ORIENTATION:
|
||||
case FEATURE_TOUCHSCREEN:
|
||||
case FEATURE_VIRTUAL_KEYBOARD:
|
||||
case FEATURE_TEXT_TO_SPEECH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String DisplayServerAndroid::get_name() const {
|
||||
return "Android";
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::tts_is_speaking() const {
|
||||
return TTS_Android::is_speaking();
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::tts_is_paused() const {
|
||||
return TTS_Android::is_paused();
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> DisplayServerAndroid::tts_get_voices() const {
|
||||
return TTS_Android::get_voices();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
|
||||
TTS_Android::speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::tts_pause() {
|
||||
TTS_Android::pause();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::tts_resume() {
|
||||
TTS_Android::resume();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::tts_stop() {
|
||||
TTS_Android::stop();
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::is_dark_mode_supported() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, false);
|
||||
|
||||
return godot_java->is_dark_mode_supported();
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::is_dark_mode() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, false);
|
||||
|
||||
return godot_java->is_dark_mode();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::set_system_theme_change_callback(const Callable &p_callable) {
|
||||
system_theme_changed = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_system_theme_changed() {
|
||||
if (system_theme_changed.is_valid()) {
|
||||
system_theme_changed.call_deferred();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::set_hardware_keyboard_connection_change_callback(const Callable &p_callable) {
|
||||
hardware_keyboard_connection_changed = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_hardware_keyboard_connection_changed(bool p_connected) {
|
||||
if (hardware_keyboard_connection_changed.is_valid()) {
|
||||
hardware_keyboard_connection_changed.call_deferred(p_connected);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::clipboard_set(const String &p_text) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL(godot_java);
|
||||
|
||||
if (godot_java->has_set_clipboard()) {
|
||||
godot_java->set_clipboard(p_text);
|
||||
} else {
|
||||
DisplayServer::clipboard_set(p_text);
|
||||
}
|
||||
}
|
||||
|
||||
String DisplayServerAndroid::clipboard_get() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, String());
|
||||
|
||||
if (godot_java->has_get_clipboard()) {
|
||||
return godot_java->get_clipboard();
|
||||
} else {
|
||||
return DisplayServer::clipboard_get();
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::clipboard_has() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, false);
|
||||
|
||||
if (godot_java->has_has_clipboard()) {
|
||||
return godot_java->has_clipboard();
|
||||
} else {
|
||||
return DisplayServer::clipboard_has();
|
||||
}
|
||||
}
|
||||
|
||||
Error DisplayServerAndroid::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||
dialog_callback = p_callback;
|
||||
return godot_java->show_dialog(p_title, p_description, p_buttons);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_dialog_callback(int p_button_index) {
|
||||
if (dialog_callback.is_valid()) {
|
||||
dialog_callback.call_deferred(p_button_index);
|
||||
}
|
||||
}
|
||||
|
||||
Error DisplayServerAndroid::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||
input_dialog_callback = p_callback;
|
||||
return godot_java->show_input_dialog(p_title, p_description, p_partial);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_input_dialog_callback(String p_text) {
|
||||
if (input_dialog_callback.is_valid()) {
|
||||
input_dialog_callback.call_deferred(p_text);
|
||||
}
|
||||
}
|
||||
|
||||
Error DisplayServerAndroid::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||
file_picker_callback = p_callback;
|
||||
return godot_java->show_file_picker(p_current_directory, p_filename, p_mode, p_filters);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_file_picker_callback(bool p_ok, const Vector<String> &p_selected_paths) {
|
||||
if (file_picker_callback.is_valid()) {
|
||||
file_picker_callback.call_deferred(p_ok, p_selected_paths, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Color DisplayServerAndroid::get_accent_color() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, Color(0, 0, 0, 0));
|
||||
return godot_java->get_accent_color();
|
||||
}
|
||||
|
||||
Color DisplayServerAndroid::get_base_color() const {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, Color(0, 0, 0, 0));
|
||||
return godot_java->get_base_color();
|
||||
}
|
||||
|
||||
TypedArray<Rect2> DisplayServerAndroid::get_display_cutouts() const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, Array());
|
||||
return godot_io_java->get_display_cutouts();
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAndroid::get_display_safe_area() const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, Rect2i());
|
||||
return godot_io_java->get_display_safe_area();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::screen_set_keep_on(bool p_enable) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL(godot_java);
|
||||
|
||||
godot_java->set_keep_screen_on(p_enable);
|
||||
keep_screen_on = p_enable;
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::screen_is_kept_on() const {
|
||||
return keep_screen_on;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX(p_screen, screen_count);
|
||||
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL(godot_io_java);
|
||||
|
||||
godot_io_java->set_screen_orientation(p_orientation);
|
||||
}
|
||||
|
||||
DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_LANDSCAPE);
|
||||
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, SCREEN_LANDSCAPE);
|
||||
|
||||
const int orientation = godot_io_java->get_screen_orientation();
|
||||
ERR_FAIL_INDEX_V_MSG(orientation, 7, SCREEN_LANDSCAPE, "Unrecognized screen orientation");
|
||||
return (ScreenOrientation)orientation;
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::get_display_rotation() const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, 0);
|
||||
|
||||
return godot_io_java->get_display_rotation();
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::get_screen_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::get_primary_screen() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Point2i DisplayServerAndroid::screen_get_position(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
|
||||
|
||||
return Point2i(0, 0);
|
||||
}
|
||||
|
||||
Size2i DisplayServerAndroid::screen_get_size(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
|
||||
|
||||
return OS_Android::get_singleton()->get_display_size();
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
|
||||
|
||||
Size2i display_size = OS_Android::get_singleton()->get_display_size();
|
||||
return Rect2i(0, 0, display_size.width, display_size.height);
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, 160);
|
||||
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, 160);
|
||||
|
||||
return godot_io_java->get_screen_dpi();
|
||||
}
|
||||
|
||||
float DisplayServerAndroid::screen_get_scale(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, 1.0f);
|
||||
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, 1.0f);
|
||||
|
||||
float screen_scale = godot_io_java->get_scaled_density();
|
||||
|
||||
// Update the scale to avoid cropping.
|
||||
Size2i screen_size = screen_get_size(p_screen);
|
||||
if (screen_size != Size2i()) {
|
||||
float width_scale = screen_size.width / (float)OS_Android::DEFAULT_WINDOW_WIDTH;
|
||||
float height_scale = screen_size.height / (float)OS_Android::DEFAULT_WINDOW_HEIGHT;
|
||||
screen_scale = MIN(screen_scale, MIN(width_scale, height_scale));
|
||||
}
|
||||
|
||||
return screen_scale;
|
||||
}
|
||||
|
||||
float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
|
||||
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
if (!godot_io_java) {
|
||||
ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
|
||||
return SCREEN_REFRESH_RATE_FALLBACK;
|
||||
}
|
||||
|
||||
return godot_io_java->get_screen_refresh_rate(SCREEN_REFRESH_RATE_FALLBACK);
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::is_touchscreen_available() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL(godot_io_java);
|
||||
|
||||
if (godot_io_java->has_vk()) {
|
||||
godot_io_java->show_vk(p_existing_text, (int)p_type, p_max_length, p_cursor_start, p_cursor_end);
|
||||
} else {
|
||||
ERR_PRINT("Virtual keyboard not available");
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::virtual_keyboard_hide() {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL(godot_io_java);
|
||||
|
||||
if (godot_io_java->has_vk()) {
|
||||
godot_io_java->hide_vk();
|
||||
} else {
|
||||
ERR_PRINT("Virtual keyboard not available");
|
||||
}
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::virtual_keyboard_get_height() const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, 0);
|
||||
|
||||
return godot_io_java->get_vk_height();
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::has_hardware_keyboard() const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, false);
|
||||
|
||||
return godot_io_java->has_hardware_keyboard();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
|
||||
window_event_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
|
||||
input_event_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_input_text_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
|
||||
input_text_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
|
||||
rect_changed_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::_window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred) const {
|
||||
if (p_callable.is_valid()) {
|
||||
if (p_deferred) {
|
||||
p_callable.call_deferred(p_arg);
|
||||
} else {
|
||||
p_callable.call(p_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::send_window_event(DisplayServer::WindowEvent p_event, bool p_deferred) const {
|
||||
_window_callback(window_event_callback, int(p_event), p_deferred);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::send_input_event(const Ref<InputEvent> &p_event) const {
|
||||
_window_callback(input_event_callback, p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::send_input_text(const String &p_text) const {
|
||||
_window_callback(input_text_callback, p_text);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::_dispatch_input_events(const Ref<InputEvent> &p_event) {
|
||||
DisplayServerAndroid::get_singleton()->send_input_event(p_event);
|
||||
}
|
||||
|
||||
Vector<DisplayServer::WindowID> DisplayServerAndroid::get_window_list() const {
|
||||
Vector<WindowID> ret;
|
||||
ret.push_back(MAIN_WINDOW_ID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DisplayServer::WindowID DisplayServerAndroid::get_window_at_screen_position(const Point2i &p_position) const {
|
||||
return MAIN_WINDOW_ID;
|
||||
}
|
||||
|
||||
int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
|
||||
switch (p_handle_type) {
|
||||
case WINDOW_HANDLE: {
|
||||
return reinterpret_cast<int64_t>(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
|
||||
}
|
||||
case WINDOW_VIEW: {
|
||||
return 0; // Not supported.
|
||||
}
|
||||
#ifdef GLES3_ENABLED
|
||||
case DISPLAY_HANDLE: {
|
||||
if (rendering_driver == "opengl3") {
|
||||
return reinterpret_cast<int64_t>(eglGetCurrentDisplay());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case OPENGL_CONTEXT: {
|
||||
if (rendering_driver == "opengl3") {
|
||||
return reinterpret_cast<int64_t>(eglGetCurrentContext());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case EGL_DISPLAY: {
|
||||
// @todo Find a way to get this from the Java side.
|
||||
return 0;
|
||||
}
|
||||
case EGL_CONFIG: {
|
||||
// @todo Find a way to get this from the Java side.
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_attach_instance_id(ObjectID p_instance, DisplayServer::WindowID p_window) {
|
||||
window_attached_instance_id = p_instance;
|
||||
}
|
||||
|
||||
ObjectID DisplayServerAndroid::window_get_attached_instance_id(DisplayServer::WindowID p_window) const {
|
||||
return window_attached_instance_id;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_title(const String &p_title, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::window_get_current_screen(DisplayServer::WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, INVALID_SCREEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
Point2i DisplayServerAndroid::window_get_position(DisplayServer::WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
Point2i DisplayServerAndroid::window_get_position_with_decorations(DisplayServer::WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_transient(DisplayServer::WindowID p_window, DisplayServer::WindowID p_parent) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
Size2i DisplayServerAndroid::window_get_max_size(DisplayServer::WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
Size2i DisplayServerAndroid::window_get_min_size(DisplayServer::WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
Size2i DisplayServerAndroid::window_get_size(DisplayServer::WindowID p_window) const {
|
||||
return OS_Android::get_singleton()->get_display_size();
|
||||
}
|
||||
|
||||
Size2i DisplayServerAndroid::window_get_size_with_decorations(DisplayServer::WindowID p_window) const {
|
||||
return OS_Android::get_singleton()->get_display_size();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_mode(DisplayServer::WindowMode p_mode, DisplayServer::WindowID p_window) {
|
||||
OS_Android::get_singleton()->get_godot_java()->enable_immersive_mode(p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN);
|
||||
}
|
||||
|
||||
DisplayServer::WindowMode DisplayServerAndroid::window_get_mode(DisplayServer::WindowID p_window) const {
|
||||
if (OS_Android::get_singleton()->get_godot_java()->is_in_immersive_mode()) {
|
||||
return WINDOW_MODE_FULLSCREEN;
|
||||
} else {
|
||||
return WINDOW_MODE_MAXIMIZED;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::window_is_maximize_allowed(DisplayServer::WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_flag(DisplayServer::WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, false);
|
||||
switch (p_flag) {
|
||||
case WindowFlags::WINDOW_FLAG_TRANSPARENT:
|
||||
return is_window_transparency_available();
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_request_attention(DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_move_to_foreground(DisplayServer::WindowID p_window) {
|
||||
// Not supported on Android.
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::window_is_focused(WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::window_can_draw(DisplayServer::WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::can_any_window_draw() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::process_events() {
|
||||
Input::get_singleton()->flush_buffered_events();
|
||||
}
|
||||
|
||||
Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
drivers.push_back("opengl3");
|
||||
#endif
|
||||
#ifdef VULKAN_ENABLED
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
|
||||
return drivers;
|
||||
}
|
||||
|
||||
DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
|
||||
DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
|
||||
if (r_error != OK) {
|
||||
if (p_rendering_driver == "vulkan") {
|
||||
OS::get_singleton()->alert(
|
||||
"Your device seems not to support the required Vulkan version.\n\n"
|
||||
"Please try exporting your game using the 'gl_compatibility' renderer.",
|
||||
"Unable to initialize Vulkan video driver");
|
||||
} else {
|
||||
OS::get_singleton()->alert(
|
||||
"Your device seems not to support the required OpenGL ES 3.0 version.",
|
||||
"Unable to initialize OpenGL video driver");
|
||||
}
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::register_android_driver() {
|
||||
register_create_function("android", create_func, get_rendering_drivers_func);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::reset_window() {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
if (rendering_device) {
|
||||
rendering_device->screen_free(MAIN_WINDOW_ID);
|
||||
}
|
||||
|
||||
VSyncMode last_vsync_mode = rendering_context->window_get_vsync_mode(MAIN_WINDOW_ID);
|
||||
rendering_context->window_destroy(MAIN_WINDOW_ID);
|
||||
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanAndroid::WindowPlatformData vulkan;
|
||||
#endif
|
||||
} wpd;
|
||||
#ifdef VULKAN_ENABLED
|
||||
if (rendering_driver == "vulkan") {
|
||||
ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
|
||||
ERR_FAIL_NULL(native_window);
|
||||
wpd.vulkan.window = native_window;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) {
|
||||
ERR_PRINT(vformat("Failed to reset %s window.", rendering_driver));
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Size2i display_size = OS_Android::get_singleton()->get_display_size();
|
||||
rendering_context->window_set_size(MAIN_WINDOW_ID, display_size.width, display_size.height);
|
||||
rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, last_vsync_mode);
|
||||
|
||||
if (rendering_device) {
|
||||
rendering_device->screen_create(MAIN_WINDOW_ID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
|
||||
if (rect_changed_callback.is_valid()) {
|
||||
rect_changed_callback.call(Rect2i(0, 0, p_width, p_height));
|
||||
}
|
||||
}
|
||||
|
||||
DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
|
||||
rendering_driver = p_rendering_driver;
|
||||
|
||||
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
|
||||
|
||||
native_menu = memnew(NativeMenu);
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
rendering_context = nullptr;
|
||||
rendering_device = nullptr;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
if (rendering_driver == "vulkan") {
|
||||
rendering_context = memnew(RenderingContextDriverVulkanAndroid);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
#if defined(GLES3_ENABLED)
|
||||
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
|
||||
if (fallback_to_opengl3 && rendering_driver != "opengl3") {
|
||||
WARN_PRINT("Your device does not seem to support Vulkan, switching to OpenGL 3.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_method("gl_compatibility");
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanAndroid::WindowPlatformData vulkan;
|
||||
#endif
|
||||
} wpd;
|
||||
#ifdef VULKAN_ENABLED
|
||||
if (rendering_driver == "vulkan") {
|
||||
ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
|
||||
ERR_FAIL_NULL(native_window);
|
||||
wpd.vulkan.window = native_window;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) {
|
||||
ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
Size2i display_size = OS_Android::get_singleton()->get_display_size();
|
||||
rendering_context->window_set_size(MAIN_WINDOW_ID, display_size.width, display_size.height);
|
||||
rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode);
|
||||
|
||||
rendering_device = memnew(RenderingDevice);
|
||||
if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {
|
||||
rendering_device = nullptr;
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
rendering_device->screen_create(MAIN_WINDOW_ID);
|
||||
|
||||
RendererCompositorRD::make_current();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3") {
|
||||
RasterizerGLES3::make_current(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
|
||||
|
||||
r_error = OK;
|
||||
}
|
||||
|
||||
DisplayServerAndroid::~DisplayServerAndroid() {
|
||||
if (native_menu) {
|
||||
memdelete(native_menu);
|
||||
native_menu = nullptr;
|
||||
}
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device) {
|
||||
memdelete(rendering_device);
|
||||
}
|
||||
if (rendering_context) {
|
||||
memdelete(rendering_context);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::process_accelerometer(const Vector3 &p_accelerometer) {
|
||||
Input::get_singleton()->set_accelerometer(p_accelerometer);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::process_gravity(const Vector3 &p_gravity) {
|
||||
Input::get_singleton()->set_gravity(p_gravity);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) {
|
||||
Input::get_singleton()->set_magnetometer(p_magnetometer);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
|
||||
Input::get_singleton()->set_gyroscope(p_gyroscope);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::_mouse_update_mode() {
|
||||
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
|
||||
? mouse_mode_override
|
||||
: mouse_mode_base;
|
||||
|
||||
if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon() || !OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_capture_pointer()) {
|
||||
return;
|
||||
}
|
||||
if (mouse_mode == wanted_mouse_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wanted_mouse_mode == MouseMode::MOUSE_MODE_HIDDEN) {
|
||||
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(CURSOR_TYPE_NULL);
|
||||
} else {
|
||||
cursor_set_shape(cursor_shape);
|
||||
}
|
||||
|
||||
if (wanted_mouse_mode == MouseMode::MOUSE_MODE_CAPTURED) {
|
||||
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
|
||||
} else {
|
||||
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->release_pointer_capture();
|
||||
}
|
||||
|
||||
mouse_mode = wanted_mouse_mode;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
|
||||
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
|
||||
if (p_mode == mouse_mode_base) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_base = p_mode;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
DisplayServer::MouseMode DisplayServerAndroid::mouse_get_mode() const {
|
||||
return mouse_mode;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::mouse_set_mode_override(MouseMode p_mode) {
|
||||
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
|
||||
if (p_mode == mouse_mode_override) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_override = p_mode;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
DisplayServer::MouseMode DisplayServerAndroid::mouse_get_mode_override() const {
|
||||
return mouse_mode_override;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::mouse_set_mode_override_enabled(bool p_override_enabled) {
|
||||
mouse_mode_override_enabled = p_override_enabled;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::mouse_is_mode_override_enabled() const {
|
||||
return mouse_mode_override_enabled;
|
||||
}
|
||||
|
||||
Point2i DisplayServerAndroid::mouse_get_position() const {
|
||||
return Input::get_singleton()->get_mouse_position();
|
||||
}
|
||||
|
||||
BitField<MouseButtonMask> DisplayServerAndroid::mouse_get_button_state() const {
|
||||
return Input::get_singleton()->get_mouse_button_mask();
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::_cursor_set_shape_helper(CursorShape p_shape, bool force) {
|
||||
if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon()) {
|
||||
return;
|
||||
}
|
||||
if (cursor_shape == p_shape && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
cursor_shape = p_shape;
|
||||
|
||||
if (mouse_mode == MouseMode::MOUSE_MODE_VISIBLE || mouse_mode == MouseMode::MOUSE_MODE_CONFINED) {
|
||||
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(android_cursors[cursor_shape]);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) {
|
||||
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
|
||||
_cursor_set_shape_helper(p_shape);
|
||||
}
|
||||
|
||||
DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const {
|
||||
return cursor_shape;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
|
||||
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
|
||||
String cursor_path = p_cursor.is_valid() ? p_cursor->get_path() : "";
|
||||
if (!cursor_path.is_empty()) {
|
||||
cursor_path = ProjectSettings::get_singleton()->globalize_path(cursor_path);
|
||||
}
|
||||
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->configure_pointer_icon(android_cursors[cursor_shape], cursor_path, p_hotspot);
|
||||
_cursor_set_shape_helper(p_shape, true);
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServer::VSyncMode DisplayServerAndroid::window_get_vsync_mode(WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
#endif
|
||||
return DisplayServer::VSYNC_ENABLED;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::reset_swap_buffers_flag() {
|
||||
swap_buffers_flag = false;
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::should_swap_buffers() const {
|
||||
return swap_buffers_flag;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::swap_buffers() {
|
||||
swap_buffers_flag = true;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::set_native_icon(const String &p_filename) {
|
||||
// NOT SUPPORTED
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::set_icon(const Ref<Image> &p_icon) {
|
||||
// NOT SUPPORTED
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::is_window_transparency_available() const {
|
||||
return GLOBAL_GET_CACHED(bool, "display/window/per_pixel_transparency/allowed");
|
||||
}
|
263
platform/android/display_server_android.h
Normal file
263
platform/android/display_server_android.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_android.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
class RenderingContextDriver;
|
||||
class RenderingDevice;
|
||||
#endif
|
||||
|
||||
class DisplayServerAndroid : public DisplayServer {
|
||||
GDSOFTCLASS(DisplayServerAndroid, DisplayServer);
|
||||
|
||||
String rendering_driver;
|
||||
|
||||
// https://developer.android.com/reference/android/view/PointerIcon
|
||||
// mapping between Godot's cursor shape to Android's'
|
||||
int android_cursors[CURSOR_MAX] = {
|
||||
1000, //CURSOR_ARROW
|
||||
1008, //CURSOR_IBEAM
|
||||
1002, //CURSOR_POINTIN
|
||||
1007, //CURSOR_CROSS
|
||||
1004, //CURSOR_WAIT
|
||||
1004, //CURSOR_BUSY
|
||||
1021, //CURSOR_DRAG
|
||||
1021, //CURSOR_CAN_DRO
|
||||
1000, //CURSOR_FORBIDD (no corresponding icon in Android's icon so fallback to default)
|
||||
1015, //CURSOR_VSIZE
|
||||
1014, //CURSOR_HSIZE
|
||||
1017, //CURSOR_BDIAGSI
|
||||
1016, //CURSOR_FDIAGSI
|
||||
1020, //CURSOR_MOVE
|
||||
1015, //CURSOR_VSPLIT
|
||||
1014, //CURSOR_HSPLIT
|
||||
1003, //CURSOR_HELP
|
||||
};
|
||||
const int CURSOR_TYPE_NULL = 0;
|
||||
MouseMode mouse_mode = MouseMode::MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_base = MouseMode::MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_override = MouseMode::MOUSE_MODE_VISIBLE;
|
||||
bool mouse_mode_override_enabled = false;
|
||||
void _mouse_update_mode();
|
||||
|
||||
bool keep_screen_on;
|
||||
bool swap_buffers_flag;
|
||||
|
||||
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
NativeMenu *native_menu = nullptr;
|
||||
|
||||
ObjectID window_attached_instance_id;
|
||||
|
||||
Callable window_event_callback;
|
||||
Callable input_event_callback;
|
||||
Callable input_text_callback;
|
||||
Callable rect_changed_callback;
|
||||
|
||||
Callable system_theme_changed;
|
||||
Callable hardware_keyboard_connection_changed;
|
||||
|
||||
Callable dialog_callback;
|
||||
Callable input_dialog_callback;
|
||||
|
||||
Callable file_picker_callback;
|
||||
|
||||
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
|
||||
|
||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||
|
||||
public:
|
||||
static DisplayServerAndroid *get_singleton();
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
virtual String get_name() const override;
|
||||
|
||||
virtual bool tts_is_speaking() const override;
|
||||
virtual bool tts_is_paused() const override;
|
||||
virtual TypedArray<Dictionary> tts_get_voices() const override;
|
||||
|
||||
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
|
||||
virtual void tts_pause() override;
|
||||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
|
||||
void emit_system_theme_changed();
|
||||
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
virtual bool clipboard_has() const override;
|
||||
|
||||
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
|
||||
void emit_dialog_callback(int p_button_index);
|
||||
|
||||
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||
void emit_input_dialog_callback(String p_text);
|
||||
|
||||
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, const FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) override;
|
||||
void emit_file_picker_callback(bool p_ok, const Vector<String> &p_selected_paths);
|
||||
|
||||
virtual Color get_accent_color() const override;
|
||||
virtual Color get_base_color() const override;
|
||||
|
||||
virtual TypedArray<Rect2> get_display_cutouts() const override;
|
||||
virtual Rect2i get_display_safe_area() const override;
|
||||
|
||||
virtual void screen_set_keep_on(bool p_enable) override;
|
||||
virtual bool screen_is_kept_on() const override;
|
||||
|
||||
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
|
||||
virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
int get_display_rotation() const;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual bool is_touchscreen_available() const override;
|
||||
|
||||
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), VirtualKeyboardType p_type = KEYBOARD_TYPE_DEFAULT, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
|
||||
virtual void virtual_keyboard_hide() override;
|
||||
virtual int virtual_keyboard_get_height() const override;
|
||||
virtual bool has_hardware_keyboard() const override;
|
||||
virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) override;
|
||||
void emit_hardware_keyboard_connection_changed(bool p_connected);
|
||||
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
void send_window_event(WindowEvent p_event, bool p_deferred = false) const;
|
||||
void send_input_event(const Ref<InputEvent> &p_event) const;
|
||||
void send_input_text(const String &p_text) const;
|
||||
|
||||
virtual Vector<WindowID> get_window_list() const override;
|
||||
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
|
||||
|
||||
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool can_any_window_draw() const override;
|
||||
|
||||
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
void process_accelerometer(const Vector3 &p_accelerometer);
|
||||
void process_gravity(const Vector3 &p_gravity);
|
||||
void process_magnetometer(const Vector3 &p_magnetometer);
|
||||
void process_gyroscope(const Vector3 &p_gyroscope);
|
||||
|
||||
void _cursor_set_shape_helper(CursorShape p_shape, bool force = false);
|
||||
virtual void cursor_set_shape(CursorShape p_shape) override;
|
||||
virtual CursorShape cursor_get_shape() const override;
|
||||
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
|
||||
|
||||
virtual void mouse_set_mode(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode() const override;
|
||||
virtual void mouse_set_mode_override(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode_override() const override;
|
||||
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
|
||||
virtual bool mouse_is_mode_override_enabled() const override;
|
||||
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
static void register_android_driver();
|
||||
|
||||
void reset_window();
|
||||
void notify_surface_changed(int p_width, int p_height);
|
||||
|
||||
virtual Point2i mouse_get_position() const override;
|
||||
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
|
||||
|
||||
void reset_swap_buffers_flag();
|
||||
bool should_swap_buffers() const;
|
||||
virtual void swap_buffers() override;
|
||||
|
||||
virtual void set_native_icon(const String &p_filename) override;
|
||||
virtual void set_icon(const Ref<Image> &p_icon) override;
|
||||
|
||||
virtual bool is_window_transparency_available() const override;
|
||||
|
||||
DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerAndroid();
|
||||
};
|
644
platform/android/doc_classes/EditorExportPlatformAndroid.xml
Normal file
644
platform/android/doc_classes/EditorExportPlatformAndroid.xml
Normal file
@@ -0,0 +1,644 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="EditorExportPlatformAndroid" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Exporter for Android.
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Exporting for Android">$DOCS_URL/tutorials/export/exporting_for_android.html</link>
|
||||
<link title="Gradle builds for Android">$DOCS_URL/tutorials/export/android_gradle_build.html</link>
|
||||
<link title="Android plugins documentation index">$DOCS_URL/tutorials/platform/index.html</link>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="apk_expansion/SALT" type="String" setter="" getter="">
|
||||
Array of random bytes that the licensing Policy uses to create an [url=https://developer.android.com/google/play/licensing/adding-licensing#impl-Obfuscator]Obfuscator[/url].
|
||||
</member>
|
||||
<member name="apk_expansion/enable" type="bool" setter="" getter="">
|
||||
If [code]true[/code], project resources are stored in the separate APK expansion file, instead of the APK.
|
||||
[b]Note:[/b] APK expansion should be enabled to use PCK encryption. See [url=https://developer.android.com/google/play/expansion-files]APK Expansion Files[/url]
|
||||
</member>
|
||||
<member name="apk_expansion/public_key" type="String" setter="" getter="">
|
||||
Base64 encoded RSA public key for your publisher account, available from the profile page on the "Google Play Console".
|
||||
</member>
|
||||
<member name="architectures/arm64-v8a" type="bool" setter="" getter="">
|
||||
If [code]true[/code], [code]arm64[/code] binaries are included into exported project.
|
||||
</member>
|
||||
<member name="architectures/armeabi-v7a" type="bool" setter="" getter="">
|
||||
If [code]true[/code], [code]arm32[/code] binaries are included into exported project.
|
||||
</member>
|
||||
<member name="architectures/x86" type="bool" setter="" getter="">
|
||||
If [code]true[/code], [code]x86_32[/code] binaries are included into exported project.
|
||||
</member>
|
||||
<member name="architectures/x86_64" type="bool" setter="" getter="">
|
||||
If [code]true[/code], [code]x86_64[/code] binaries are included into exported project.
|
||||
</member>
|
||||
<member name="command_line/extra_args" type="String" setter="" getter="">
|
||||
A list of additional command line arguments, separated by space, which the exported project will receive when started.
|
||||
</member>
|
||||
<member name="custom_template/debug" type="String" setter="" getter="">
|
||||
Path to an APK file to use as a custom export template for debug exports. If left empty, default template is used.
|
||||
[b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
|
||||
</member>
|
||||
<member name="custom_template/release" type="String" setter="" getter="">
|
||||
Path to an APK file to use as a custom export template for release exports. If left empty, default template is used.
|
||||
[b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
|
||||
</member>
|
||||
<member name="gesture/swipe_to_dismiss" type="bool" setter="" getter="">
|
||||
If [code]true[/code], [url=https://developer.android.com/design/ui/wear/guides/components/swipe-to-dismiss]Swipe to dismiss[/url] will be enabled.
|
||||
This functionality is intended for smartwatches and is generally ignored on standard Android devices. However, some devices may not ignore it. Therefore, it is recommended to keep this feature disabled for standard Android apps to avoid unexpected behavior.
|
||||
[b]Note:[/b] This is [code]false[/code] by default. To enable this behavior, [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is required.
|
||||
</member>
|
||||
<member name="gradle_build/android_source_template" type="String" setter="" getter="">
|
||||
Path to a ZIP file holding the source for the export template used in a Gradle build. If left empty, the default template is used.
|
||||
</member>
|
||||
<member name="gradle_build/compress_native_libraries" type="bool" setter="" getter="">
|
||||
If [code]true[/code], native libraries are compressed when performing a Gradle build.
|
||||
[b]Note:[/b] While enabling compression can reduce the size of the binary, it may result in slower application startup because the native libraries must be extracted before use, rather than being loaded directly.
|
||||
If you're distributing your app via the Play Store, it's generally recommended to keep this option [code]false[/code], see [url=https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs]official documentation[/url].
|
||||
</member>
|
||||
<member name="gradle_build/custom_theme_attributes" type="Dictionary" setter="" getter="">
|
||||
A dictionary of custom theme attributes to include in the exported Android project. Each entry defines a theme attribute name and its value, and will be added to the [b]GodotAppMainTheme[/b].
|
||||
For example, the key [code]android:windowSwipeToDismiss[/code] with the value [code]false[/code] is resolved to [code]<item name="android:windowSwipeToDismiss">false</item>[/code].
|
||||
[b]Note:[/b] To add a custom attribute to the [b]GodotAppSplashTheme[/b], prefix the attribute name with [code][splash][/code].
|
||||
[b]Note:[/b] Reserved attributes configured via other export options or project settings cannot be overridden by [code]custom_theme_attributes[/code] and are skipped during export.
|
||||
</member>
|
||||
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
||||
Application export format (*.apk or *.aab).
|
||||
</member>
|
||||
<member name="gradle_build/gradle_build_directory" type="String" setter="" getter="">
|
||||
Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
|
||||
</member>
|
||||
<member name="gradle_build/min_sdk" type="String" setter="" getter="">
|
||||
Minimum Android API level required for the application to run (used during Gradle build). See [url=https://developer.android.com/guide/topics/manifest/uses-sdk-element#uses]android:minSdkVersion[/url].
|
||||
</member>
|
||||
<member name="gradle_build/target_sdk" type="String" setter="" getter="">
|
||||
The Android API level on which the application is designed to run (used during Gradle build). See [url=https://developer.android.com/guide/topics/manifest/uses-sdk-element#uses]android:targetSdkVersion[/url].
|
||||
</member>
|
||||
<member name="gradle_build/use_gradle_build" type="bool" setter="" getter="">
|
||||
If [code]true[/code], Gradle build is used instead of pre-built APK.
|
||||
</member>
|
||||
<member name="graphics/opengl_debug" type="bool" setter="" getter="">
|
||||
If [code]true[/code], OpenGL ES debug context will be created (additional runtime checking, validation, and logging).
|
||||
</member>
|
||||
<member name="keystore/debug" type="String" setter="" getter="">
|
||||
Path of the debug keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PATH[/code].
|
||||
Fallbacks to [code]EditorSettings.export/android/debug_keystore[/code] if empty.
|
||||
</member>
|
||||
<member name="keystore/debug_password" type="String" setter="" getter="">
|
||||
Password for the debug keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD[/code].
|
||||
Fallbacks to [code]EditorSettings.export/android/debug_keystore_pass[/code] if both it and [member keystore/debug] are empty.
|
||||
</member>
|
||||
<member name="keystore/debug_user" type="String" setter="" getter="">
|
||||
User name for the debug keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_USER[/code].
|
||||
Fallbacks to [code]EditorSettings.export/android/debug_keystore_user[/code] if both it and [member keystore/debug] are empty.
|
||||
</member>
|
||||
<member name="keystore/release" type="String" setter="" getter="">
|
||||
Path of the release keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PATH[/code].
|
||||
</member>
|
||||
<member name="keystore/release_password" type="String" setter="" getter="">
|
||||
Password for the release keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD[/code].
|
||||
</member>
|
||||
<member name="keystore/release_user" type="String" setter="" getter="">
|
||||
User name for the release keystore file.
|
||||
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code].
|
||||
</member>
|
||||
<member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter="">
|
||||
Background layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
|
||||
</member>
|
||||
<member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter="">
|
||||
Foreground layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
|
||||
</member>
|
||||
<member name="launcher_icons/adaptive_monochrome_432x432" type="String" setter="" getter="">
|
||||
Monochrome layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
|
||||
</member>
|
||||
<member name="launcher_icons/main_192x192" type="String" setter="" getter="">
|
||||
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
|
||||
</member>
|
||||
<member name="package/app_category" type="int" setter="" getter="">
|
||||
Application category for the Google Play Store. Only define this if your application fits one of the categories well. See [url=https://developer.android.com/guide/topics/manifest/application-element#appCategory]android:appCategory[/url].
|
||||
</member>
|
||||
<member name="package/exclude_from_recents" type="bool" setter="" getter="">
|
||||
If [code]true[/code], task initiated by main activity will be excluded from the list of recently used applications. See [url=https://developer.android.com/guide/topics/manifest/activity-element#exclude]android:excludeFromRecents[/url].
|
||||
</member>
|
||||
<member name="package/name" type="String" setter="" getter="">
|
||||
Name of the application.
|
||||
</member>
|
||||
<member name="package/retain_data_on_uninstall" type="bool" setter="" getter="">
|
||||
If [code]true[/code], when the user uninstalls an app, a prompt to keep the app's data will be shown. See [url=https://developer.android.com/guide/topics/manifest/application-element#fragileuserdata]android:hasFragileUserData[/url].
|
||||
</member>
|
||||
<member name="package/show_as_launcher_app" type="bool" setter="" getter="">
|
||||
If [code]true[/code], the user will be able to set this app as the system launcher in Android preferences.
|
||||
</member>
|
||||
<member name="package/show_in_android_tv" type="bool" setter="" getter="">
|
||||
If [code]true[/code], this app will show in Android TV launcher UI.
|
||||
</member>
|
||||
<member name="package/show_in_app_library" type="bool" setter="" getter="">
|
||||
If [code]true[/code], this app will show in the device's app library.
|
||||
[b]Note:[/b] This is [code]true[/code] by default.
|
||||
</member>
|
||||
<member name="package/signed" type="bool" setter="" getter="">
|
||||
If [code]true[/code], package signing is enabled.
|
||||
</member>
|
||||
<member name="package/unique_name" type="String" setter="" getter="">
|
||||
Unique application identifier in a reverse-DNS format. The reverse DNS format should preferably match a domain name you control, but this is not strictly required. For instance, if you own [code]example.com[/code], your package unique name should preferably be of the form [code]com.example.mygame[/code]. This identifier can only contain lowercase alphanumeric characters ([code]a-z[/code], and [code]0-9[/code]), underscores ([code]_[/code]), and periods ([code].[/code]). Each component of the reverse DNS format must start with a letter: for instance, [code]com.example.8game[/code] is not valid.
|
||||
If [code]$genname[/code] is present in the value, it will be replaced by the project name converted to lowercase. If there are invalid characters in the project name, they will be stripped. If all characters in the project name are stripped, [code]$genname[/code] is replaced by [code]noname[/code].
|
||||
[b]Note:[/b] Changing the package name will cause the package to be considered as a new package, with its own installation and data paths. The new package won't be usable to update existing installations.
|
||||
[b]Note:[/b] When publishing to Google Play, the package name must be [i]globally[/i] unique. This means no other apps published on Google Play must be using the same package name as yours. Otherwise, you'll be prevented from publishing your app on Google Play.
|
||||
</member>
|
||||
<member name="permissions/access_checkin_properties" type="bool" setter="" getter="">
|
||||
Allows read/write access to the "properties" table in the checkin database. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_CHECKIN_PROPERTIES]ACCESS_CHECKIN_PROPERTIES[/url].
|
||||
</member>
|
||||
<member name="permissions/access_coarse_location" type="bool" setter="" getter="">
|
||||
Allows access to the approximate location information. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION]ACCESS_COARSE_LOCATION[/url].
|
||||
</member>
|
||||
<member name="permissions/access_fine_location" type="bool" setter="" getter="">
|
||||
Allows access to the precise location information. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION]ACCESS_FINE_LOCATION[/url].
|
||||
</member>
|
||||
<member name="permissions/access_location_extra_commands" type="bool" setter="" getter="">
|
||||
Allows access to the extra location provider commands. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_LOCATION_EXTRA_COMMANDS]ACCESS_LOCATION_EXTRA_COMMANDS[/url].
|
||||
</member>
|
||||
<member name="permissions/access_media_location" type="bool" setter="" getter="">
|
||||
Allows an application to access any geographic locations persisted in the user's shared collection. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_MEDIA_LOCATION]ACCESS_MEDIA_LOCATION[/url].
|
||||
</member>
|
||||
<member name="permissions/access_mock_location" type="bool" setter="" getter="">
|
||||
Allows an application to create mock location providers for testing.
|
||||
</member>
|
||||
<member name="permissions/access_network_state" type="bool" setter="" getter="">
|
||||
Allows access to the information about networks. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_NETWORK_STATE]ACCESS_NETWORK_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/access_surface_flinger" type="bool" setter="" getter="">
|
||||
Allows an application to use SurfaceFlinger's low level features.
|
||||
</member>
|
||||
<member name="permissions/access_wifi_state" type="bool" setter="" getter="">
|
||||
Allows access to the information about Wi-Fi networks. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCESS_WIFI_STATE]ACCESS_WIFI_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/account_manager" type="bool" setter="" getter="">
|
||||
Allows applications to call into AccountAuthenticators. See [url=https://developer.android.com/reference/android/Manifest.permission#ACCOUNT_MANAGER]ACCOUNT_MANAGER[/url].
|
||||
</member>
|
||||
<member name="permissions/add_voicemail" type="bool" setter="" getter="">
|
||||
Allows an application to add voicemails into the system. See [url=https://developer.android.com/reference/android/Manifest.permission#ADD_VOICEMAIL]ADD_VOICEMAIL[/url].
|
||||
</member>
|
||||
<member name="permissions/authenticate_accounts" type="bool" setter="" getter="">
|
||||
Allows an application to act as an AccountAuthenticator for the AccountManager.
|
||||
</member>
|
||||
<member name="permissions/battery_stats" type="bool" setter="" getter="">
|
||||
Allows an application to collect battery statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_accessibility_service" type="bool" setter="" getter="">
|
||||
Must be required by an AccessibilityService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_ACCESSIBILITY_SERVICE]BIND_ACCESSIBILITY_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_appwidget" type="bool" setter="" getter="">
|
||||
Allows an application to tell the AppWidget service which application can access AppWidget's data. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_APPWIDGET]BIND_APPWIDGET[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_device_admin" type="bool" setter="" getter="">
|
||||
Must be required by device administration receiver, to ensure that only the system can interact with it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_DEVICE_ADMIN]BIND_DEVICE_ADMIN[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_input_method" type="bool" setter="" getter="">
|
||||
Must be required by an InputMethodService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_INPUT_METHOD]BIND_INPUT_METHOD[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_nfc_service" type="bool" setter="" getter="">
|
||||
Must be required by a HostApduService or OffHostApduService to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NFC_SERVICE]BIND_NFC_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_notification_listener_service" type="bool" setter="" getter="">
|
||||
Must be required by a NotificationListenerService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE]BIND_NOTIFICATION_LISTENER_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_print_service" type="bool" setter="" getter="">
|
||||
Must be required by a PrintService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_PRINT_SERVICE]BIND_PRINT_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_remoteviews" type="bool" setter="" getter="">
|
||||
Must be required by a RemoteViewsService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_REMOTEVIEWS]BIND_REMOTEVIEWS[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_text_service" type="bool" setter="" getter="">
|
||||
Must be required by a TextService (e.g. SpellCheckerService) to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_TEXT_SERVICE]BIND_TEXT_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_vpn_service" type="bool" setter="" getter="">
|
||||
Must be required by a VpnService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_VPN_SERVICE]BIND_VPN_SERVICE[/url].
|
||||
</member>
|
||||
<member name="permissions/bind_wallpaper" type="bool" setter="" getter="">
|
||||
Must be required by a WallpaperService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_WALLPAPER]BIND_WALLPAPER[/url].
|
||||
</member>
|
||||
<member name="permissions/bluetooth" type="bool" setter="" getter="">
|
||||
Allows applications to connect to paired bluetooth devices. See [url=https://developer.android.com/reference/android/Manifest.permission#BLUETOOTH]BLUETOOTH[/url].
|
||||
</member>
|
||||
<member name="permissions/bluetooth_admin" type="bool" setter="" getter="">
|
||||
Allows applications to discover and pair bluetooth devices. See [url=https://developer.android.com/reference/android/Manifest.permission#BLUETOOTH_ADMIN]BLUETOOTH_ADMIN[/url].
|
||||
</member>
|
||||
<member name="permissions/bluetooth_privileged" type="bool" setter="" getter="">
|
||||
Allows applications to pair bluetooth devices without user interaction, and to allow or disallow phonebook access or message access. See [url=https://developer.android.com/reference/android/Manifest.permission#BLUETOOTH_PRIVILEGED]BLUETOOTH_PRIVILEGED[/url].
|
||||
</member>
|
||||
<member name="permissions/brick" type="bool" setter="" getter="">
|
||||
Required to be able to disable the device (very dangerous!).
|
||||
</member>
|
||||
<member name="permissions/broadcast_package_removed" type="bool" setter="" getter="">
|
||||
Allows an application to broadcast a notification that an application package has been removed. See [url=https://developer.android.com/reference/android/Manifest.permission#BROADCAST_PACKAGE_REMOVED]BROADCAST_PACKAGE_REMOVED[/url].
|
||||
</member>
|
||||
<member name="permissions/broadcast_sms" type="bool" setter="" getter="">
|
||||
Allows an application to broadcast an SMS receipt notification. See [url=https://developer.android.com/reference/android/Manifest.permission#BROADCAST_SMS]BROADCAST_SMS[/url].
|
||||
</member>
|
||||
<member name="permissions/broadcast_sticky" type="bool" setter="" getter="">
|
||||
Allows an application to broadcast sticky intents. See [url=https://developer.android.com/reference/android/Manifest.permission#BROADCAST_STICKY]BROADCAST_STICKY[/url].
|
||||
</member>
|
||||
<member name="permissions/broadcast_wap_push" type="bool" setter="" getter="">
|
||||
Allows an application to broadcast a WAP PUSH receipt notification. See [url=https://developer.android.com/reference/android/Manifest.permission#BROADCAST_WAP_PUSH]BROADCAST_WAP_PUSH[/url].
|
||||
</member>
|
||||
<member name="permissions/call_phone" type="bool" setter="" getter="">
|
||||
Allows an application to initiate a phone call without going through the Dialer user interface. See [url=https://developer.android.com/reference/android/Manifest.permission#CALL_PHONE]CALL_PHONE[/url].
|
||||
</member>
|
||||
<member name="permissions/call_privileged" type="bool" setter="" getter="">
|
||||
Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface. See [url=https://developer.android.com/reference/android/Manifest.permission#CALL_PRIVILEGED]CALL_PRIVILEGED[/url].
|
||||
</member>
|
||||
<member name="permissions/camera" type="bool" setter="" getter="">
|
||||
Required to be able to access the camera device. See [url=https://developer.android.com/reference/android/Manifest.permission#CAMERA]CAMERA[/url].
|
||||
</member>
|
||||
<member name="permissions/capture_audio_output" type="bool" setter="" getter="">
|
||||
Allows an application to capture audio output. See [url=https://developer.android.com/reference/android/Manifest.permission#CAPTURE_AUDIO_OUTPUT]CAPTURE_AUDIO_OUTPUT[/url].
|
||||
</member>
|
||||
<member name="permissions/capture_secure_video_output" type="bool" setter="" getter="">
|
||||
Allows an application to capture secure video output.
|
||||
</member>
|
||||
<member name="permissions/capture_video_output" type="bool" setter="" getter="">
|
||||
Allows an application to capture video output.
|
||||
</member>
|
||||
<member name="permissions/change_component_enabled_state" type="bool" setter="" getter="">
|
||||
Allows an application to change whether an application component (other than its own) is enabled or not. See [url=https://developer.android.com/reference/android/Manifest.permission#CHANGE_COMPONENT_ENABLED_STATE]CHANGE_COMPONENT_ENABLED_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/change_configuration" type="bool" setter="" getter="">
|
||||
Allows an application to modify the current configuration, such as locale. See [url=https://developer.android.com/reference/android/Manifest.permission#CHANGE_CONFIGURATION]CHANGE_CONFIGURATION[/url].
|
||||
</member>
|
||||
<member name="permissions/change_network_state" type="bool" setter="" getter="">
|
||||
Allows applications to change network connectivity state. See [url=https://developer.android.com/reference/android/Manifest.permission#CHANGE_NETWORK_STATE]CHANGE_NETWORK_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/change_wifi_multicast_state" type="bool" setter="" getter="">
|
||||
Allows applications to enter Wi-Fi Multicast mode. See [url=https://developer.android.com/reference/android/Manifest.permission#CHANGE_WIFI_MULTICAST_STATE]CHANGE_WIFI_MULTICAST_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/change_wifi_state" type="bool" setter="" getter="">
|
||||
Allows applications to change Wi-Fi connectivity state. See [url=https://developer.android.com/reference/android/Manifest.permission#CHANGE_WIFI_STATE]CHANGE_WIFI_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/clear_app_cache" type="bool" setter="" getter="">
|
||||
Allows an application to clear the caches of all installed applications on the device. See [url=https://developer.android.com/reference/android/Manifest.permission#CLEAR_APP_CACHE]CLEAR_APP_CACHE[/url].
|
||||
</member>
|
||||
<member name="permissions/clear_app_user_data" type="bool" setter="" getter="">
|
||||
Allows an application to clear user data.
|
||||
</member>
|
||||
<member name="permissions/control_location_updates" type="bool" setter="" getter="">
|
||||
Allows enabling/disabling location update notifications from the radio. See [url=https://developer.android.com/reference/android/Manifest.permission#CONTROL_LOCATION_UPDATES]CONTROL_LOCATION_UPDATES[/url].
|
||||
</member>
|
||||
<member name="permissions/custom_permissions" type="PackedStringArray" setter="" getter="">
|
||||
Array of custom permission strings.
|
||||
</member>
|
||||
<member name="permissions/delete_cache_files" type="bool" setter="" getter="" deprecated="">
|
||||
</member>
|
||||
<member name="permissions/delete_packages" type="bool" setter="" getter="">
|
||||
Allows an application to delete packages. See [url=https://developer.android.com/reference/android/Manifest.permission#DELETE_PACKAGES]DELETE_PACKAGES[/url].
|
||||
</member>
|
||||
<member name="permissions/device_power" type="bool" setter="" getter="">
|
||||
Allows low-level access to power management.
|
||||
</member>
|
||||
<member name="permissions/diagnostic" type="bool" setter="" getter="">
|
||||
Allows applications to RW to diagnostic resources. See [url=https://developer.android.com/reference/android/Manifest.permission#DIAGNOSTIC]DIAGNOSTIC[/url].
|
||||
</member>
|
||||
<member name="permissions/disable_keyguard" type="bool" setter="" getter="">
|
||||
Allows applications to disable the keyguard if it is not secure. See [url=https://developer.android.com/reference/android/Manifest.permission#DISABLE_KEYGUARD]DISABLE_KEYGUARD[/url].
|
||||
</member>
|
||||
<member name="permissions/dump" type="bool" setter="" getter="">
|
||||
Allows an application to retrieve state dump information from system services. See [url=https://developer.android.com/reference/android/Manifest.permission#DUMP]DUMP[/url].
|
||||
</member>
|
||||
<member name="permissions/expand_status_bar" type="bool" setter="" getter="">
|
||||
Allows an application to expand or collapse the status bar. See [url=https://developer.android.com/reference/android/Manifest.permission#EXPAND_STATUS_BAR]EXPAND_STATUS_BAR[/url].
|
||||
</member>
|
||||
<member name="permissions/factory_test" type="bool" setter="" getter="">
|
||||
Run as a manufacturer test application, running as the root user. See [url=https://developer.android.com/reference/android/Manifest.permission#FACTORY_TEST]FACTORY_TEST[/url].
|
||||
</member>
|
||||
<member name="permissions/flashlight" type="bool" setter="" getter="">
|
||||
Allows access to the flashlight.
|
||||
</member>
|
||||
<member name="permissions/force_back" type="bool" setter="" getter="">
|
||||
Allows an application to force a BACK operation on whatever is the top activity.
|
||||
</member>
|
||||
<member name="permissions/get_accounts" type="bool" setter="" getter="">
|
||||
Allows access to the list of accounts in the Accounts Service. See [url=https://developer.android.com/reference/android/Manifest.permission#GET_ACCOUNTS]GET_ACCOUNTS[/url].
|
||||
</member>
|
||||
<member name="permissions/get_package_size" type="bool" setter="" getter="">
|
||||
Allows an application to find out the space used by any package. See [url=https://developer.android.com/reference/android/Manifest.permission#GET_PACKAGE_SIZE]GET_PACKAGE_SIZE[/url].
|
||||
</member>
|
||||
<member name="permissions/get_tasks" type="bool" setter="" getter="" deprecated="Deprecated in API level 21.">
|
||||
</member>
|
||||
<member name="permissions/get_top_activity_info" type="bool" setter="" getter="">
|
||||
Allows an application to retrieve private information about the current top activity.
|
||||
</member>
|
||||
<member name="permissions/global_search" type="bool" setter="" getter="">
|
||||
Used on content providers to allow the global search system to access their data. See [url=https://developer.android.com/reference/android/Manifest.permission#GLOBAL_SEARCH]GLOBAL_SEARCH[/url].
|
||||
</member>
|
||||
<member name="permissions/hardware_test" type="bool" setter="" getter="">
|
||||
Allows access to hardware peripherals.
|
||||
</member>
|
||||
<member name="permissions/inject_events" type="bool" setter="" getter="">
|
||||
Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.
|
||||
</member>
|
||||
<member name="permissions/install_location_provider" type="bool" setter="" getter="">
|
||||
Allows an application to install a location provider into the Location Manager. See [url=https://developer.android.com/reference/android/Manifest.permission#INSTALL_LOCATION_PROVIDER]INSTALL_LOCATION_PROVIDER[/url].
|
||||
</member>
|
||||
<member name="permissions/install_packages" type="bool" setter="" getter="">
|
||||
Allows an application to install packages. See [url=https://developer.android.com/reference/android/Manifest.permission#INSTALL_PACKAGES]INSTALL_PACKAGES[/url].
|
||||
</member>
|
||||
<member name="permissions/install_shortcut" type="bool" setter="" getter="">
|
||||
Allows an application to install a shortcut in Launcher. See [url=https://developer.android.com/reference/android/Manifest.permission#INSTALL_SHORTCUT]INSTALL_SHORTCUT[/url].
|
||||
</member>
|
||||
<member name="permissions/internal_system_window" type="bool" setter="" getter="">
|
||||
Allows an application to open windows that are for use by parts of the system user interface.
|
||||
</member>
|
||||
<member name="permissions/internet" type="bool" setter="" getter="">
|
||||
Allows applications to open network sockets. See [url=https://developer.android.com/reference/android/Manifest.permission#INTERNET]INTERNET[/url].
|
||||
</member>
|
||||
<member name="permissions/kill_background_processes" type="bool" setter="" getter="">
|
||||
Allows an application to call ActivityManager.killBackgroundProcesses(String). See [url=https://developer.android.com/reference/android/Manifest.permission#KILL_BACKGROUND_PROCESSES]KILL_BACKGROUND_PROCESSES[/url].
|
||||
</member>
|
||||
<member name="permissions/location_hardware" type="bool" setter="" getter="">
|
||||
Allows an application to use location features in hardware, such as the geofencing api. See [url=https://developer.android.com/reference/android/Manifest.permission#LOCATION_HARDWARE]LOCATION_HARDWARE[/url].
|
||||
</member>
|
||||
<member name="permissions/manage_accounts" type="bool" setter="" getter="">
|
||||
Allows an application to manage the list of accounts in the AccountManager.
|
||||
</member>
|
||||
<member name="permissions/manage_app_tokens" type="bool" setter="" getter="">
|
||||
Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.
|
||||
</member>
|
||||
<member name="permissions/manage_documents" type="bool" setter="" getter="">
|
||||
Allows an application to manage access to documents, usually as part of a document picker. See [url=https://developer.android.com/reference/android/Manifest.permission#MANAGE_DOCUMENTS]MANAGE_DOCUMENTS[/url].
|
||||
</member>
|
||||
<member name="permissions/manage_external_storage" type="bool" setter="" getter="">
|
||||
Allows an application a broad access to external storage in scoped storage. See [url=https://developer.android.com/reference/android/Manifest.permission#MANAGE_EXTERNAL_STORAGE]MANAGE_EXTERNAL_STORAGE[/url].
|
||||
</member>
|
||||
<member name="permissions/master_clear" type="bool" setter="" getter="">
|
||||
See [url=https://developer.android.com/reference/android/Manifest.permission#MASTER_CLEAR]MASTER_CLEAR[/url].
|
||||
</member>
|
||||
<member name="permissions/media_content_control" type="bool" setter="" getter="">
|
||||
Allows an application to know what content is playing and control its playback. See [url=https://developer.android.com/reference/android/Manifest.permission#MEDIA_CONTENT_CONTROL]MEDIA_CONTENT_CONTROL[/url].
|
||||
</member>
|
||||
<member name="permissions/modify_audio_settings" type="bool" setter="" getter="">
|
||||
Allows an application to modify global audio settings. See [url=https://developer.android.com/reference/android/Manifest.permission#MODIFY_AUDIO_SETTINGS]MODIFY_AUDIO_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/modify_phone_state" type="bool" setter="" getter="">
|
||||
Allows modification of the telephony state - power on, mmi, etc. Does not include placing calls. See [url=https://developer.android.com/reference/android/Manifest.permission#MODIFY_PHONE_STATE]MODIFY_PHONE_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/mount_format_filesystems" type="bool" setter="" getter="">
|
||||
Allows formatting file systems for removable storage. See [url=https://developer.android.com/reference/android/Manifest.permission#MOUNT_FORMAT_FILESYSTEMS]MOUNT_FORMAT_FILESYSTEMS[/url].
|
||||
</member>
|
||||
<member name="permissions/mount_unmount_filesystems" type="bool" setter="" getter="">
|
||||
Allows mounting and unmounting file systems for removable storage. See [url=https://developer.android.com/reference/android/Manifest.permission#MOUNT_UNMOUNT_FILESYSTEMS]MOUNT_UNMOUNT_FILESYSTEMS[/url].
|
||||
</member>
|
||||
<member name="permissions/nfc" type="bool" setter="" getter="">
|
||||
Allows applications to perform I/O operations over NFC. See [url=https://developer.android.com/reference/android/Manifest.permission#NFC]NFC[/url].
|
||||
</member>
|
||||
<member name="permissions/persistent_activity" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
|
||||
Allows an application to make its activities persistent.
|
||||
</member>
|
||||
<member name="permissions/post_notifications" type="bool" setter="" getter="">
|
||||
Allows an application to post notifications. Added in API level 33. See [url=https://developer.android.com/develop/ui/views/notifications/notification-permission]Notification runtime permission[/url].
|
||||
</member>
|
||||
<member name="permissions/process_outgoing_calls" type="bool" setter="" getter="" deprecated="Deprecated in API level 29.">
|
||||
Allows an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. See [url=https://developer.android.com/reference/android/Manifest.permission#PROCESS_OUTGOING_CALLS]PROCESS_OUTGOING_CALLS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_calendar" type="bool" setter="" getter="">
|
||||
Allows an application to read the user's calendar data. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_CALENDAR]READ_CALENDAR[/url].
|
||||
</member>
|
||||
<member name="permissions/read_call_log" type="bool" setter="" getter="">
|
||||
Allows an application to read the user's call log. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_CALL_LOG]READ_CALL_LOG[/url].
|
||||
</member>
|
||||
<member name="permissions/read_contacts" type="bool" setter="" getter="">
|
||||
Allows an application to read the user's contacts data. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_CONTACTS]READ_CONTACTS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_external_storage" type="bool" setter="" getter="" deprecated="Deprecated in API level 33.">
|
||||
Allows an application to read from external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE]READ_EXTERNAL_STORAGE[/url].
|
||||
</member>
|
||||
<member name="permissions/read_frame_buffer" type="bool" setter="" getter="">
|
||||
Allows an application to take screen shots and more generally get access to the frame buffer data.
|
||||
</member>
|
||||
<member name="permissions/read_history_bookmarks" type="bool" setter="" getter="">
|
||||
Allows an application to read (but not write) the user's browsing history and bookmarks.
|
||||
</member>
|
||||
<member name="permissions/read_input_state" type="bool" setter="" getter="" deprecated="Deprecated in API level 16.">
|
||||
</member>
|
||||
<member name="permissions/read_logs" type="bool" setter="" getter="">
|
||||
Allows an application to read the low-level system log files. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_LOGS]READ_LOGS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_media_audio" type="bool" setter="" getter="">
|
||||
Allows an application to read audio files from external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_AUDIO]READ_MEDIA_AUDIO[/url].
|
||||
</member>
|
||||
<member name="permissions/read_media_images" type="bool" setter="" getter="">
|
||||
Allows an application to read image files from external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_IMAGES]READ_MEDIA_IMAGES[/url].
|
||||
</member>
|
||||
<member name="permissions/read_media_video" type="bool" setter="" getter="">
|
||||
Allows an application to read video files from external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_VIDEO]READ_MEDIA_VIDEO[/url].
|
||||
</member>
|
||||
<member name="permissions/read_media_visual_user_selected" type="bool" setter="" getter="">
|
||||
Allows an application to read image or video files from external storage that a user has selected via the permission prompt photo picker. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_VISUAL_USER_SELECTED]READ_MEDIA_VISUAL_USER_SELECTED[/url].
|
||||
</member>
|
||||
<member name="permissions/read_phone_state" type="bool" setter="" getter="">
|
||||
Allows read only access to phone state. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_PHONE_STATE]READ_PHONE_STATE[/url].
|
||||
</member>
|
||||
<member name="permissions/read_profile" type="bool" setter="" getter="">
|
||||
Allows an application to read the user's personal profile data.
|
||||
</member>
|
||||
<member name="permissions/read_sms" type="bool" setter="" getter="">
|
||||
Allows an application to read SMS messages. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_SMS]READ_SMS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_social_stream" type="bool" setter="" getter="">
|
||||
Allows an application to read from the user's social stream.
|
||||
</member>
|
||||
<member name="permissions/read_sync_settings" type="bool" setter="" getter="">
|
||||
Allows applications to read the sync settings. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_SYNC_SETTINGS]READ_SYNC_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_sync_stats" type="bool" setter="" getter="">
|
||||
Allows applications to read the sync stats. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_SYNC_STATS]READ_SYNC_STATS[/url].
|
||||
</member>
|
||||
<member name="permissions/read_user_dictionary" type="bool" setter="" getter="">
|
||||
Allows an application to read the user dictionary.
|
||||
</member>
|
||||
<member name="permissions/reboot" type="bool" setter="" getter="">
|
||||
Required to be able to reboot the device. See [url=https://developer.android.com/reference/android/Manifest.permission#REBOOT]REBOOT[/url].
|
||||
</member>
|
||||
<member name="permissions/receive_boot_completed" type="bool" setter="" getter="">
|
||||
Allows an application to receive the Intent.ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting. See [url=https://developer.android.com/reference/android/Manifest.permission#RECEIVE_BOOT_COMPLETED]RECEIVE_BOOT_COMPLETED[/url].
|
||||
</member>
|
||||
<member name="permissions/receive_mms" type="bool" setter="" getter="">
|
||||
Allows an application to monitor incoming MMS messages. See [url=https://developer.android.com/reference/android/Manifest.permission#RECEIVE_MMS]RECEIVE_MMS[/url].
|
||||
</member>
|
||||
<member name="permissions/receive_sms" type="bool" setter="" getter="">
|
||||
Allows an application to receive SMS messages. See [url=https://developer.android.com/reference/android/Manifest.permission#RECEIVE_SMS]RECEIVE_SMS[/url].
|
||||
</member>
|
||||
<member name="permissions/receive_wap_push" type="bool" setter="" getter="">
|
||||
Allows an application to receive WAP push messages. See [url=https://developer.android.com/reference/android/Manifest.permission#RECEIVE_WAP_PUSH]RECEIVE_WAP_PUSH[/url].
|
||||
</member>
|
||||
<member name="permissions/record_audio" type="bool" setter="" getter="">
|
||||
Allows an application to record audio. See [url=https://developer.android.com/reference/android/Manifest.permission#RECORD_AUDIO]RECORD_AUDIO[/url].
|
||||
</member>
|
||||
<member name="permissions/reorder_tasks" type="bool" setter="" getter="">
|
||||
Allows an application to change the Z-order of tasks. See [url=https://developer.android.com/reference/android/Manifest.permission#REORDER_TASKS]REORDER_TASKS[/url].
|
||||
</member>
|
||||
<member name="permissions/restart_packages" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
|
||||
</member>
|
||||
<member name="permissions/send_respond_via_message" type="bool" setter="" getter="">
|
||||
Allows an application (Phone) to send a request to other applications to handle the respond-via-message action during incoming calls. See [url=https://developer.android.com/reference/android/Manifest.permission#SEND_RESPOND_VIA_MESSAGE]SEND_RESPOND_VIA_MESSAGE[/url].
|
||||
</member>
|
||||
<member name="permissions/send_sms" type="bool" setter="" getter="">
|
||||
Allows an application to send SMS messages. See [url=https://developer.android.com/reference/android/Manifest.permission#SEND_SMS]SEND_SMS[/url].
|
||||
</member>
|
||||
<member name="permissions/set_activity_watcher" type="bool" setter="" getter="">
|
||||
Allows an application to watch and control how activities are started globally in the system.
|
||||
</member>
|
||||
<member name="permissions/set_alarm" type="bool" setter="" getter="">
|
||||
Allows an application to broadcast an Intent to set an alarm for the user. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_ALARM]SET_ALARM[/url].
|
||||
</member>
|
||||
<member name="permissions/set_always_finish" type="bool" setter="" getter="">
|
||||
Allows an application to control whether activities are immediately finished when put in the background. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_ALWAYS_FINISH]SET_ALWAYS_FINISH[/url].
|
||||
</member>
|
||||
<member name="permissions/set_animation_scale" type="bool" setter="" getter="">
|
||||
Allows to modify the global animation scaling factor. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_ANIMATION_SCALE]SET_ANIMATION_SCALE[/url].
|
||||
</member>
|
||||
<member name="permissions/set_debug_app" type="bool" setter="" getter="">
|
||||
Configure an application for debugging. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_DEBUG_APP]SET_DEBUG_APP[/url].
|
||||
</member>
|
||||
<member name="permissions/set_orientation" type="bool" setter="" getter="">
|
||||
Allows low-level access to setting the orientation (actually rotation) of the screen.
|
||||
</member>
|
||||
<member name="permissions/set_pointer_speed" type="bool" setter="" getter="">
|
||||
Allows low-level access to setting the pointer speed.
|
||||
</member>
|
||||
<member name="permissions/set_preferred_applications" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
|
||||
</member>
|
||||
<member name="permissions/set_process_limit" type="bool" setter="" getter="">
|
||||
Allows an application to set the maximum number of (not needed) application processes that can be running. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_PROCESS_LIMIT]SET_PROCESS_LIMIT[/url].
|
||||
</member>
|
||||
<member name="permissions/set_time" type="bool" setter="" getter="">
|
||||
Allows applications to set the system time directly. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_TIME]SET_TIME[/url].
|
||||
</member>
|
||||
<member name="permissions/set_time_zone" type="bool" setter="" getter="">
|
||||
Allows applications to set the system time zone directly. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_TIME_ZONE]SET_TIME_ZONE[/url].
|
||||
</member>
|
||||
<member name="permissions/set_wallpaper" type="bool" setter="" getter="">
|
||||
Allows applications to set the wallpaper. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_WALLPAPER]SET_WALLPAPER[/url].
|
||||
</member>
|
||||
<member name="permissions/set_wallpaper_hints" type="bool" setter="" getter="">
|
||||
Allows applications to set the wallpaper hints. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_WALLPAPER_HINTS]SET_WALLPAPER_HINTS[/url].
|
||||
</member>
|
||||
<member name="permissions/signal_persistent_processes" type="bool" setter="" getter="">
|
||||
Allow an application to request that a signal be sent to all persistent processes. See [url=https://developer.android.com/reference/android/Manifest.permission#SIGNAL_PERSISTENT_PROCESSES]SIGNAL_PERSISTENT_PROCESSES[/url].
|
||||
</member>
|
||||
<member name="permissions/status_bar" type="bool" setter="" getter="">
|
||||
Allows an application to open, close, or disable the status bar and its icons. See [url=https://developer.android.com/reference/android/Manifest.permission#STATUS_BAR]STATUS_BAR[/url].
|
||||
</member>
|
||||
<member name="permissions/subscribed_feeds_read" type="bool" setter="" getter="">
|
||||
Allows an application to allow access the subscribed feeds ContentProvider.
|
||||
</member>
|
||||
<member name="permissions/subscribed_feeds_write" type="bool" setter="" getter="" deprecated="">
|
||||
</member>
|
||||
<member name="permissions/system_alert_window" type="bool" setter="" getter="">
|
||||
Allows an app to create windows using the type WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, shown on top of all other apps. See [url=https://developer.android.com/reference/android/Manifest.permission#SYSTEM_ALERT_WINDOW]SYSTEM_ALERT_WINDOW[/url].
|
||||
</member>
|
||||
<member name="permissions/transmit_ir" type="bool" setter="" getter="">
|
||||
Allows using the device's IR transmitter, if available. See [url=https://developer.android.com/reference/android/Manifest.permission#TRANSMIT_IR]TRANSMIT_IR[/url].
|
||||
</member>
|
||||
<member name="permissions/uninstall_shortcut" type="bool" setter="" getter="" deprecated="">
|
||||
</member>
|
||||
<member name="permissions/update_device_stats" type="bool" setter="" getter="">
|
||||
Allows an application to update device statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#UPDATE_DEVICE_STATS]UPDATE_DEVICE_STATS[/url].
|
||||
</member>
|
||||
<member name="permissions/use_credentials" type="bool" setter="" getter="">
|
||||
Allows an application to request authtokens from the AccountManager.
|
||||
</member>
|
||||
<member name="permissions/use_sip" type="bool" setter="" getter="">
|
||||
Allows an application to use SIP service. See [url=https://developer.android.com/reference/android/Manifest.permission#USE_SIP]USE_SIP[/url].
|
||||
</member>
|
||||
<member name="permissions/vibrate" type="bool" setter="" getter="">
|
||||
Allows access to the vibrator. See [url=https://developer.android.com/reference/android/Manifest.permission#VIBRATE]VIBRATE[/url].
|
||||
</member>
|
||||
<member name="permissions/wake_lock" type="bool" setter="" getter="">
|
||||
Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. See [url=https://developer.android.com/reference/android/Manifest.permission#WAKE_LOCK]WAKE_LOCK[/url].
|
||||
</member>
|
||||
<member name="permissions/write_apn_settings" type="bool" setter="" getter="">
|
||||
Allows applications to write the apn settings and read sensitive fields of an existing apn settings like user and password. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_APN_SETTINGS]WRITE_APN_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/write_calendar" type="bool" setter="" getter="">
|
||||
Allows an application to write the user's calendar data. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_CALENDAR]WRITE_CALENDAR[/url].
|
||||
</member>
|
||||
<member name="permissions/write_call_log" type="bool" setter="" getter="">
|
||||
Allows an application to write (but not read) the user's call log data. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_CALL_LOG]WRITE_CALL_LOG[/url].
|
||||
</member>
|
||||
<member name="permissions/write_contacts" type="bool" setter="" getter="">
|
||||
Allows an application to write the user's contacts data. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_CONTACTS]WRITE_CONTACTS[/url].
|
||||
</member>
|
||||
<member name="permissions/write_external_storage" type="bool" setter="" getter="">
|
||||
Allows an application to write to external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE]WRITE_EXTERNAL_STORAGE[/url].
|
||||
</member>
|
||||
<member name="permissions/write_gservices" type="bool" setter="" getter="">
|
||||
Allows an application to modify the Google service map. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_GSERVICES]WRITE_GSERVICES[/url].
|
||||
</member>
|
||||
<member name="permissions/write_history_bookmarks" type="bool" setter="" getter="">
|
||||
Allows an application to write (but not read) the user's browsing history and bookmarks.
|
||||
</member>
|
||||
<member name="permissions/write_profile" type="bool" setter="" getter="">
|
||||
Allows an application to write (but not read) the user's personal profile data.
|
||||
</member>
|
||||
<member name="permissions/write_secure_settings" type="bool" setter="" getter="">
|
||||
Allows an application to read or write the secure system settings. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_SECURE_SETTINGS]WRITE_SECURE_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/write_settings" type="bool" setter="" getter="">
|
||||
Allows an application to read or write the system settings. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_SETTINGS]WRITE_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/write_sms" type="bool" setter="" getter="">
|
||||
Allows an application to write SMS messages.
|
||||
</member>
|
||||
<member name="permissions/write_social_stream" type="bool" setter="" getter="">
|
||||
Allows an application to write (but not read) the user's social stream data.
|
||||
</member>
|
||||
<member name="permissions/write_sync_settings" type="bool" setter="" getter="">
|
||||
Allows applications to write the sync settings. See [url=https://developer.android.com/reference/android/Manifest.permission#WRITE_SYNC_SETTINGS]WRITE_SYNC_SETTINGS[/url].
|
||||
</member>
|
||||
<member name="permissions/write_user_dictionary" type="bool" setter="" getter="">
|
||||
Allows an application to write to the user dictionary.
|
||||
</member>
|
||||
<member name="screen/background_color" type="Color" setter="" getter="">
|
||||
The background color used for the root window. Default is [code]black[/code].
|
||||
</member>
|
||||
<member name="screen/edge_to_edge" type="bool" setter="" getter="">
|
||||
If [code]true[/code], this makes the navigation and status bars translucent and allows the application content to extend edge to edge.
|
||||
[b]Note:[/b] You should ensure that none of the application content is occluded by system elements by using the [method DisplayServer.get_display_safe_area] and [method DisplayServer.get_display_cutouts] methods.
|
||||
</member>
|
||||
<member name="screen/immersive_mode" type="bool" setter="" getter="">
|
||||
If [code]true[/code], hides the navigation and status bar. Set [method DisplayServer.window_set_mode] to change this at runtime.
|
||||
</member>
|
||||
<member name="screen/support_large" type="bool" setter="" getter="">
|
||||
Indicates whether the application supports larger screen form-factors.
|
||||
</member>
|
||||
<member name="screen/support_normal" type="bool" setter="" getter="">
|
||||
Indicates whether an application supports the "normal" screen form-factors.
|
||||
</member>
|
||||
<member name="screen/support_small" type="bool" setter="" getter="">
|
||||
Indicates whether the application supports smaller screen form-factors.
|
||||
</member>
|
||||
<member name="screen/support_xlarge" type="bool" setter="" getter="">
|
||||
Indicates whether the application supports extra large screen form-factors.
|
||||
</member>
|
||||
<member name="shader_baker/enabled" type="bool" setter="" getter="">
|
||||
If [code]true[/code], shaders will be compiled and embedded in the application. This option is only supported when using the Forward+ or Mobile renderers.
|
||||
</member>
|
||||
<member name="user_data_backup/allow" type="bool" setter="" getter="">
|
||||
If [code]true[/code], allows the application to participate in the backup and restore infrastructure.
|
||||
</member>
|
||||
<member name="version/code" type="int" setter="" getter="">
|
||||
Machine-readable application version. This must be incremented for every new release pushed to the Play Store.
|
||||
</member>
|
||||
<member name="version/name" type="String" setter="" getter="">
|
||||
Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty.
|
||||
</member>
|
||||
<member name="xr_features/xr_mode" type="int" setter="" getter="">
|
||||
The extended reality (XR) mode for this application.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
95
platform/android/editor/editor_utils_jni.cpp
Normal file
95
platform/android/editor/editor_utils_jni.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/**************************************************************************/
|
||||
/* editor_utils_jni.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_utils_jni.h"
|
||||
|
||||
#include "jni_utils.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/run/editor_run_bar.h"
|
||||
#include "main/main.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_EditorUtils_runScene(JNIEnv *p_env, jclass, jstring p_scene, jobjectArray p_scene_args) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
Vector<String> scene_args;
|
||||
jint length = p_env->GetArrayLength(p_scene_args);
|
||||
for (jint i = 0; i < length; ++i) {
|
||||
jstring j_arg = (jstring)p_env->GetObjectArrayElement(p_scene_args, i);
|
||||
String arg = jstring_to_string(j_arg, p_env);
|
||||
scene_args.push_back(arg);
|
||||
p_env->DeleteLocalRef(j_arg);
|
||||
}
|
||||
|
||||
String scene = jstring_to_string(p_scene, p_env);
|
||||
|
||||
EditorRunBar *editor_run_bar = EditorRunBar::get_singleton();
|
||||
if (editor_run_bar != nullptr) {
|
||||
editor_run_bar->stop_playing();
|
||||
// Ensure that all ScriptEditorDebugger instances are explicitly stopped.
|
||||
// If not, a closing instance from the previous run session will trigger `_stop_and_notify()`, in turn causing
|
||||
// the closure of the ScriptEditorDebugger instances of the run session we're about to launch.
|
||||
EditorDebuggerNode *dbg_node = EditorDebuggerNode::get_singleton();
|
||||
if (dbg_node != nullptr) {
|
||||
for (int i = 0; ScriptEditorDebugger *dbg = dbg_node->get_debugger(i); i++) {
|
||||
dbg->stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (scene.is_empty()) {
|
||||
editor_run_bar->play_main_scene(false);
|
||||
} else {
|
||||
editor_run_bar->play_custom_scene(scene, scene_args);
|
||||
}
|
||||
} else {
|
||||
List<String> args;
|
||||
|
||||
for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_PROJECT)) {
|
||||
args.push_back(a);
|
||||
}
|
||||
|
||||
for (const String &arg : scene_args) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
if (!scene.is_empty()) {
|
||||
args.push_back("--scene");
|
||||
args.push_back(scene);
|
||||
}
|
||||
|
||||
Error err = OS::get_singleton()->create_instance(args);
|
||||
ERR_FAIL_COND(err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
37
platform/android/editor/editor_utils_jni.h
Normal file
37
platform/android/editor/editor_utils_jni.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/**************************************************************************/
|
||||
/* editor_utils_jni.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_EditorUtils_runScene(JNIEnv *p_env, jclass, jstring p_scene, jobjectArray p_scene_args);
|
||||
}
|
145
platform/android/editor/game_menu_utils_jni.cpp
Normal file
145
platform/android/editor/game_menu_utils_jni.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* game_menu_utils_jni.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "game_menu_utils_jni.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/run/game_view_plugin.h"
|
||||
|
||||
static GameViewPlugin *_get_game_view_plugin() {
|
||||
ERR_FAIL_NULL_V(EditorNode::get_singleton(), nullptr);
|
||||
ERR_FAIL_NULL_V(EditorNode::get_singleton()->get_editor_main_screen(), nullptr);
|
||||
return Object::cast_to<GameViewPlugin>(EditorNode::get_singleton()->get_editor_main_screen()->get_plugin_by_name("Game"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSuspend(JNIEnv *env, jclass clazz, jboolean enabled) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_suspend(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_nextFrame(JNIEnv *env, jclass clazz) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->next_frame();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setNodeType(JNIEnv *env, jclass clazz, jint type) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_node_type(type);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSelectMode(JNIEnv *env, jclass clazz, jint mode) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_select_mode(mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSelectionVisible(JNIEnv *env, jclass clazz, jboolean visible) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_selection_visible(visible);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setCameraOverride(JNIEnv *env, jclass clazz, jboolean enabled) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_camera_override(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setCameraManipulateMode(JNIEnv *env, jclass clazz, jint mode) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_camera_manipulate_mode(static_cast<EditorDebuggerNode::CameraOverride>(mode));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera2DPosition(JNIEnv *env, jclass clazz) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->reset_camera_2d_position();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->reset_camera_3d_position();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (EditorInterface::get_singleton()) {
|
||||
EditorInterface::get_singleton()->play_main_scene();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_debug_mute_audio(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
47
platform/android/editor/game_menu_utils_jni.h
Normal file
47
platform/android/editor/game_menu_utils_jni.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**************************************************************************/
|
||||
/* game_menu_utils_jni.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSuspend(JNIEnv *env, jclass clazz, jboolean enabled);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_nextFrame(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setNodeType(JNIEnv *env, jclass clazz, jint type);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSelectMode(JNIEnv *env, jclass clazz, jint mode);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setSelectionVisible(JNIEnv *env, jclass clazz, jboolean visible);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setCameraOverride(JNIEnv *env, jclass clazz, jboolean enabled);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setCameraManipulateMode(JNIEnv *env, jclass clazz, jint mode);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera2DPosition(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled);
|
||||
}
|
87
platform/android/export/export.cpp
Normal file
87
platform/android/export/export.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/**************************************************************************/
|
||||
/* export.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#include "export_plugin.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/file_system/editor_paths.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
|
||||
String get_default_android_sdk_path();
|
||||
|
||||
void register_android_exporter_types() {
|
||||
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformAndroid);
|
||||
}
|
||||
|
||||
void register_android_exporter() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
EDITOR_DEF_BASIC("export/android/debug_keystore", EditorPaths::get_singleton()->get_debug_keystore_path());
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"));
|
||||
EDITOR_DEF_BASIC("export/android/debug_keystore_user", DEFAULT_ANDROID_KEYSTORE_DEBUG_USER);
|
||||
EDITOR_DEF_BASIC("export/android/debug_keystore_pass", DEFAULT_ANDROID_KEYSTORE_DEBUG_PASSWORD);
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore_pass", PROPERTY_HINT_PASSWORD));
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
EDITOR_DEF_BASIC("export/android/install_exported_apk", !OS::get_singleton()->has_feature("horizonos"));
|
||||
#else
|
||||
EDITOR_DEF_BASIC("export/android/java_sdk_path", OS::get_singleton()->get_environment("JAVA_HOME"));
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/java_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
|
||||
|
||||
EDITOR_DEF_BASIC("export/android/android_sdk_path", OS::get_singleton()->has_environment("ANDROID_HOME") ? OS::get_singleton()->get_environment("ANDROID_HOME") : get_default_android_sdk_path());
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
|
||||
|
||||
EDITOR_DEF("export/android/force_system_user", false);
|
||||
|
||||
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
|
||||
|
||||
EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false);
|
||||
|
||||
EDITOR_DEF("export/android/use_wifi_for_remote_debug", false);
|
||||
EDITOR_DEF("export/android/wifi_remote_debug_host", "localhost");
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
|
||||
EditorExport::get_singleton()->add_export_platform(exporter);
|
||||
}
|
||||
|
||||
inline String get_default_android_sdk_path() {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
return OS::get_singleton()->get_environment("LOCALAPPDATA").path_join("Android/Sdk");
|
||||
#elif LINUXBSD_ENABLED
|
||||
return OS::get_singleton()->get_environment("HOME").path_join("Android/Sdk");
|
||||
#elif MACOS_ENABLED
|
||||
return OS::get_singleton()->get_environment("HOME").path_join("Library/Android/sdk");
|
||||
#else
|
||||
return String();
|
||||
#endif
|
||||
}
|
34
platform/android/export/export.h
Normal file
34
platform/android/export/export.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**************************************************************************/
|
||||
/* export.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_android_exporter_types();
|
||||
void register_android_exporter();
|
4281
platform/android/export/export_plugin.cpp
Normal file
4281
platform/android/export/export_plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
285
platform/android/export/export_plugin.h
Normal file
285
platform/android/export/export_plugin.h
Normal file
@@ -0,0 +1,285 @@
|
||||
/**************************************************************************/
|
||||
/* export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "godot_plugin_config.h"
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
||||
// Optional environment variables for defining confidential information. If any
|
||||
// of these is set, they will override the values set in the credentials file.
|
||||
const String ENV_ANDROID_KEYSTORE_DEBUG_PATH = "GODOT_ANDROID_KEYSTORE_DEBUG_PATH";
|
||||
const String ENV_ANDROID_KEYSTORE_DEBUG_USER = "GODOT_ANDROID_KEYSTORE_DEBUG_USER";
|
||||
const String ENV_ANDROID_KEYSTORE_DEBUG_PASS = "GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD";
|
||||
const String ENV_ANDROID_KEYSTORE_RELEASE_PATH = "GODOT_ANDROID_KEYSTORE_RELEASE_PATH";
|
||||
const String ENV_ANDROID_KEYSTORE_RELEASE_USER = "GODOT_ANDROID_KEYSTORE_RELEASE_USER";
|
||||
const String ENV_ANDROID_KEYSTORE_RELEASE_PASS = "GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD";
|
||||
|
||||
const String DEFAULT_ANDROID_KEYSTORE_DEBUG_USER = "androiddebugkey";
|
||||
const String DEFAULT_ANDROID_KEYSTORE_DEBUG_PASSWORD = "android";
|
||||
|
||||
struct LauncherIcon {
|
||||
const char *export_path;
|
||||
int dimensions = 0;
|
||||
};
|
||||
|
||||
class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
|
||||
|
||||
Ref<ImageTexture> logo;
|
||||
Ref<ImageTexture> run_icon;
|
||||
|
||||
struct Device {
|
||||
String id;
|
||||
String name;
|
||||
String description;
|
||||
int api_level = 0;
|
||||
String architecture;
|
||||
};
|
||||
|
||||
struct APKExportData {
|
||||
EditorExportPlatform::PackData pd;
|
||||
zipFile apk;
|
||||
EditorProgress *ep = nullptr;
|
||||
};
|
||||
|
||||
struct FeatureInfo {
|
||||
String name;
|
||||
bool required;
|
||||
String version;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
mutable Vector<PluginConfigAndroid> android_plugins;
|
||||
mutable SafeFlag android_plugins_changed;
|
||||
Mutex android_plugins_lock;
|
||||
#endif // DISABLE_DEPRECATED
|
||||
String last_plugin_names;
|
||||
uint64_t last_gradle_build_time = 0;
|
||||
String last_gradle_build_dir;
|
||||
|
||||
Vector<Device> devices;
|
||||
SafeFlag devices_changed;
|
||||
Mutex device_lock;
|
||||
#ifndef ANDROID_ENABLED
|
||||
Thread check_for_changes_thread;
|
||||
SafeFlag quit_request;
|
||||
SafeFlag has_runnable_preset;
|
||||
|
||||
static void _check_for_changes_poll_thread(void *ud);
|
||||
void _update_preset_status();
|
||||
#endif
|
||||
|
||||
String get_project_name(const Ref<EditorExportPreset> &p_preset, const String &p_name) const;
|
||||
|
||||
String get_package_name(const Ref<EditorExportPreset> &p_preset, const String &p_package) const;
|
||||
|
||||
String get_valid_basename(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
|
||||
|
||||
bool is_package_name_valid(const Ref<EditorExportPreset> &p_preset, const String &p_package, String *r_error = nullptr) const;
|
||||
bool is_project_name_valid(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data);
|
||||
|
||||
static zip_fileinfo get_zip_fileinfo();
|
||||
|
||||
struct ABI {
|
||||
String abi;
|
||||
String arch;
|
||||
|
||||
bool operator==(const ABI &p_a) const {
|
||||
return p_a.abi == abi;
|
||||
}
|
||||
|
||||
ABI(const String &p_abi, const String &p_arch) {
|
||||
abi = p_abi;
|
||||
arch = p_arch;
|
||||
}
|
||||
ABI() {}
|
||||
};
|
||||
|
||||
static Vector<ABI> get_abis();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
/// List the gdap files in the directory specified by the p_path parameter.
|
||||
static Vector<String> list_gdap_files(const String &p_path);
|
||||
|
||||
static Vector<PluginConfigAndroid> get_plugins();
|
||||
|
||||
static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED);
|
||||
|
||||
static Error save_apk_so(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
|
||||
static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
|
||||
static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
bool _has_read_write_storage_permission(const Vector<String> &p_permissions);
|
||||
|
||||
bool _has_manage_external_storage_permission(const Vector<String> &p_permissions);
|
||||
|
||||
void _get_manifest_info(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions, Vector<FeatureInfo> &r_features, Vector<MetadataInfo> &r_metadata);
|
||||
|
||||
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
|
||||
|
||||
bool _is_transparency_allowed(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
void _fix_themes_xml(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
|
||||
|
||||
static String _get_keystore_path(const Ref<EditorExportPreset> &p_preset, bool p_debug);
|
||||
|
||||
static String _parse_string(const uint8_t *p_bytes, bool p_utf8);
|
||||
|
||||
void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest);
|
||||
|
||||
void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data);
|
||||
|
||||
void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data);
|
||||
|
||||
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome);
|
||||
|
||||
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
|
||||
const Ref<Image> &p_main_image,
|
||||
const Ref<Image> &p_foreground,
|
||||
const Ref<Image> &p_background,
|
||||
const Ref<Image> &p_monochrome);
|
||||
|
||||
static void _create_editor_debug_keystore_if_needed();
|
||||
|
||||
static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
bool _uses_vulkan(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
Error _generate_sparse_pck_metadata(const Ref<EditorExportPreset> &p_preset, PackData &p_pack_data, Vector<uint8_t> &r_data);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
|
||||
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
|
||||
virtual String get_name() const override;
|
||||
|
||||
virtual String get_os_name() const override;
|
||||
|
||||
virtual Ref<Texture2D> get_logo() const override;
|
||||
|
||||
virtual bool should_update_export_options() override;
|
||||
|
||||
virtual bool poll_export() override;
|
||||
|
||||
virtual int get_options_count() const override;
|
||||
|
||||
virtual String get_options_tooltip() const override;
|
||||
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
|
||||
virtual String get_device_architecture(int p_index) const override;
|
||||
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
|
||||
static String get_adb_path();
|
||||
|
||||
static String get_apksigner_path(int p_target_sdk = -1, bool p_check_executes = false);
|
||||
|
||||
static String get_java_path();
|
||||
|
||||
static String get_keytool_path();
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
static bool has_valid_username_and_password(const Ref<EditorExportPreset> &p_preset, String &r_error);
|
||||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
|
||||
String _get_deprecated_plugins_names(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String _get_plugins_names(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String _resolve_export_plugin_android_library_path(const String &p_android_library_path) const;
|
||||
|
||||
bool _is_clean_build_required(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
||||
Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
|
||||
void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags);
|
||||
|
||||
Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep);
|
||||
|
||||
void _clear_assets_directory(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
void _remove_copied_libs(String p_gdextension_libs_path);
|
||||
|
||||
static String join_list(const List<String> &p_parts, const String &p_separator);
|
||||
static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch);
|
||||
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
|
||||
Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags);
|
||||
|
||||
virtual void get_platform_features(List<String> *r_features) const override;
|
||||
|
||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override;
|
||||
|
||||
EditorExportPlatformAndroid();
|
||||
|
||||
~EditorExportPlatformAndroid();
|
||||
};
|
199
platform/android/export/godot_plugin_config.cpp
Normal file
199
platform/android/export/godot_plugin_config.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/**************************************************************************/
|
||||
/* godot_plugin_config.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "godot_plugin_config.h"
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
/*
|
||||
* Set of prebuilt plugins.
|
||||
* Currently unused, this is just for future reference:
|
||||
*/
|
||||
// static const PluginConfigAndroid MY_PREBUILT_PLUGIN = {
|
||||
// /*.valid_config =*/true,
|
||||
// /*.last_updated =*/0,
|
||||
// /*.name =*/"GodotPayment",
|
||||
// /*.binary_type =*/"local",
|
||||
// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
|
||||
// /*.local_dependencies =*/{},
|
||||
// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"),
|
||||
// /*.custom_maven_repos =*/{}
|
||||
// };
|
||||
|
||||
String PluginConfigAndroid::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
|
||||
String absolute_path;
|
||||
if (!dependency_path.is_empty()) {
|
||||
if (dependency_path.is_absolute_path()) {
|
||||
absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
|
||||
} else {
|
||||
absolute_path = plugin_config_dir.path_join(dependency_path);
|
||||
}
|
||||
}
|
||||
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
PluginConfigAndroid PluginConfigAndroid::resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) {
|
||||
PluginConfigAndroid resolved = prebuilt_plugin;
|
||||
resolved.binary = resolved.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary;
|
||||
if (!prebuilt_plugin.local_dependencies.is_empty()) {
|
||||
resolved.local_dependencies.clear();
|
||||
for (int i = 0; i < prebuilt_plugin.local_dependencies.size(); i++) {
|
||||
resolved.local_dependencies.push_back(resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.local_dependencies[i]));
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plugins_base_dir) {
|
||||
Vector<PluginConfigAndroid> prebuilt_plugins;
|
||||
return prebuilt_plugins;
|
||||
}
|
||||
|
||||
bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) {
|
||||
bool valid_name = !plugin_config.name.is_empty();
|
||||
bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ||
|
||||
plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool valid_local_dependencies = true;
|
||||
if (!plugin_config.local_dependencies.is_empty()) {
|
||||
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
|
||||
if (!FileAccess::exists(plugin_config.local_dependencies[i])) {
|
||||
valid_local_dependencies = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
|
||||
}
|
||||
|
||||
uint64_t PluginConfigAndroid::get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) {
|
||||
uint64_t last_updated = FileAccess::get_modified_time(config_path);
|
||||
last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
|
||||
|
||||
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
|
||||
String binary = plugin_config.local_dependencies.get(i);
|
||||
last_updated = MAX(last_updated, FileAccess::get_modified_time(binary));
|
||||
}
|
||||
|
||||
return last_updated;
|
||||
}
|
||||
|
||||
PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
|
||||
PluginConfigAndroid plugin_config = {};
|
||||
|
||||
if (config_file.is_valid()) {
|
||||
Error err = config_file->load(path);
|
||||
if (err == OK) {
|
||||
String config_base_dir = path.get_base_dir();
|
||||
|
||||
plugin_config.name = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_NAME_KEY, String());
|
||||
plugin_config.binary_type = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_TYPE_KEY, String());
|
||||
|
||||
String binary_path = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_KEY, String());
|
||||
plugin_config.binary = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path;
|
||||
|
||||
if (config_file->has_section(PluginConfigAndroid::DEPENDENCIES_SECTION)) {
|
||||
Vector<String> local_dependencies_paths = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_LOCAL_KEY, Vector<String>());
|
||||
if (!local_dependencies_paths.is_empty()) {
|
||||
for (int i = 0; i < local_dependencies_paths.size(); i++) {
|
||||
plugin_config.local_dependencies.push_back(resolve_local_dependency_path(config_base_dir, local_dependencies_paths[i]));
|
||||
}
|
||||
}
|
||||
|
||||
plugin_config.remote_dependencies = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY, Vector<String>());
|
||||
plugin_config.custom_maven_repos = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
|
||||
}
|
||||
|
||||
plugin_config.valid_config = is_plugin_config_valid(plugin_config);
|
||||
plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
|
||||
}
|
||||
}
|
||||
|
||||
return plugin_config;
|
||||
}
|
||||
|
||||
void PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
|
||||
if (!plugins_configs.is_empty()) {
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.binary_type == binary_type) {
|
||||
r_result.push_back(config.binary);
|
||||
}
|
||||
|
||||
if (binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL) {
|
||||
r_result.append_array(config.local_dependencies);
|
||||
}
|
||||
|
||||
if (binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE) {
|
||||
r_result.append_array(config.remote_dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
|
||||
if (!plugins_configs.is_empty()) {
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r_result.append_array(config.custom_maven_repos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
|
||||
if (!plugins_configs.is_empty()) {
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r_result.push_back(config.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
104
platform/android/export/godot_plugin_config.h
Normal file
104
platform/android/export/godot_plugin_config.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************/
|
||||
/* godot_plugin_config.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
/*
|
||||
The `config` section and fields are required and defined as follow:
|
||||
- **name**: name of the plugin.
|
||||
- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field.
|
||||
- **binary**:
|
||||
- if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`).
|
||||
- if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0").
|
||||
|
||||
The `dependencies` section and fields are optional and defined as follow:
|
||||
- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory.
|
||||
- **remote**: contains a list of remote binary gradle dependencies for the plugin.
|
||||
- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies.
|
||||
|
||||
See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871
|
||||
*/
|
||||
struct PluginConfigAndroid {
|
||||
inline static const char *PLUGIN_CONFIG_EXT = ".gdap";
|
||||
|
||||
inline static const char *CONFIG_SECTION = "config";
|
||||
inline static const char *CONFIG_NAME_KEY = "name";
|
||||
inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type";
|
||||
inline static const char *CONFIG_BINARY_KEY = "binary";
|
||||
|
||||
inline static const char *DEPENDENCIES_SECTION = "dependencies";
|
||||
inline static const char *DEPENDENCIES_LOCAL_KEY = "local";
|
||||
inline static const char *DEPENDENCIES_REMOTE_KEY = "remote";
|
||||
inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
|
||||
|
||||
inline static const char *BINARY_TYPE_LOCAL = "local";
|
||||
inline static const char *BINARY_TYPE_REMOTE = "remote";
|
||||
|
||||
// Set to true when the config file is properly loaded.
|
||||
bool valid_config = false;
|
||||
// Unix timestamp of last change to this plugin.
|
||||
uint64_t last_updated = 0;
|
||||
|
||||
// Required config section
|
||||
String name;
|
||||
String binary_type;
|
||||
String binary;
|
||||
|
||||
// Optional dependencies section
|
||||
Vector<String> local_dependencies;
|
||||
Vector<String> remote_dependencies;
|
||||
Vector<String> custom_maven_repos;
|
||||
|
||||
static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path);
|
||||
|
||||
static PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir);
|
||||
|
||||
static Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir);
|
||||
|
||||
static bool is_plugin_config_valid(PluginConfigAndroid plugin_config);
|
||||
|
||||
static uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path);
|
||||
|
||||
static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path);
|
||||
|
||||
static void get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
|
||||
|
||||
static void get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
|
||||
|
||||
static void get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
|
||||
};
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
396
platform/android/export/gradle_export_util.cpp
Normal file
396
platform/android/export/gradle_export_util.cpp
Normal file
@@ -0,0 +1,396 @@
|
||||
/**************************************************************************/
|
||||
/* gradle_export_util.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) {
|
||||
switch (screen_orientation) {
|
||||
case DisplayServer::SCREEN_PORTRAIT:
|
||||
return 1;
|
||||
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
|
||||
return 8;
|
||||
case DisplayServer::SCREEN_REVERSE_PORTRAIT:
|
||||
return 9;
|
||||
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
|
||||
return 11;
|
||||
case DisplayServer::SCREEN_SENSOR_PORTRAIT:
|
||||
return 12;
|
||||
case DisplayServer::SCREEN_SENSOR:
|
||||
return 13;
|
||||
case DisplayServer::SCREEN_LANDSCAPE:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) {
|
||||
switch (screen_orientation) {
|
||||
case DisplayServer::SCREEN_PORTRAIT:
|
||||
return "portrait";
|
||||
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
|
||||
return "reverseLandscape";
|
||||
case DisplayServer::SCREEN_REVERSE_PORTRAIT:
|
||||
return "reversePortrait";
|
||||
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
|
||||
return "userLandscape";
|
||||
case DisplayServer::SCREEN_SENSOR_PORTRAIT:
|
||||
return "userPortrait";
|
||||
case DisplayServer::SCREEN_SENSOR:
|
||||
return "fullUser";
|
||||
case DisplayServer::SCREEN_LANDSCAPE:
|
||||
default:
|
||||
return "landscape";
|
||||
}
|
||||
}
|
||||
|
||||
int _get_app_category_value(int category_index) {
|
||||
switch (category_index) {
|
||||
case APP_CATEGORY_ACCESSIBILITY:
|
||||
return 8;
|
||||
case APP_CATEGORY_AUDIO:
|
||||
return 1;
|
||||
case APP_CATEGORY_IMAGE:
|
||||
return 3;
|
||||
case APP_CATEGORY_MAPS:
|
||||
return 6;
|
||||
case APP_CATEGORY_NEWS:
|
||||
return 5;
|
||||
case APP_CATEGORY_PRODUCTIVITY:
|
||||
return 7;
|
||||
case APP_CATEGORY_SOCIAL:
|
||||
return 4;
|
||||
case APP_CATEGORY_UNDEFINED:
|
||||
return -1;
|
||||
case APP_CATEGORY_VIDEO:
|
||||
return 2;
|
||||
case APP_CATEGORY_GAME:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String _get_app_category_label(int category_index) {
|
||||
switch (category_index) {
|
||||
case APP_CATEGORY_ACCESSIBILITY:
|
||||
return "accessibility";
|
||||
case APP_CATEGORY_AUDIO:
|
||||
return "audio";
|
||||
case APP_CATEGORY_IMAGE:
|
||||
return "image";
|
||||
case APP_CATEGORY_MAPS:
|
||||
return "maps";
|
||||
case APP_CATEGORY_NEWS:
|
||||
return "news";
|
||||
case APP_CATEGORY_PRODUCTIVITY:
|
||||
return "productivity";
|
||||
case APP_CATEGORY_SOCIAL:
|
||||
return "social";
|
||||
case APP_CATEGORY_VIDEO:
|
||||
return "video";
|
||||
case APP_CATEGORY_GAME:
|
||||
default:
|
||||
return "game";
|
||||
}
|
||||
}
|
||||
|
||||
// Utility method used to create a directory.
|
||||
Error create_directory(const String &p_dir) {
|
||||
if (!DirAccess::exists(p_dir)) {
|
||||
Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
|
||||
Error err = filesystem_da->make_dir_recursive(p_dir);
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Writes p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
|
||||
String dir = p_path.get_base_dir();
|
||||
Error err = create_directory(dir);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
|
||||
fa->store_buffer(p_data.ptr(), p_data.size());
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Writes string p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_string_at_path(const String &p_path, const String &p_data) {
|
||||
String dir = p_path.get_base_dir();
|
||||
Error err = create_directory(dir);
|
||||
if (err != OK) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_error("Unable to write data into " + p_path);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
|
||||
fa->store_string(p_data);
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Implementation of EditorExportSaveFunction.
|
||||
// This method will only be called as an input to export_project_files.
|
||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||
// It's functionality mirrors that of the method save_apk_file.
|
||||
// This method will be called ONLY when gradle build is enabled.
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) {
|
||||
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
|
||||
|
||||
String simplified_path = p_path.simplify_path();
|
||||
if (simplified_path.begins_with("uid://")) {
|
||||
simplified_path = ResourceUID::uid_to_path(simplified_path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, simplified_path));
|
||||
}
|
||||
|
||||
Vector<uint8_t> enc_data;
|
||||
EditorExportPlatform::SavedData sd;
|
||||
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, enc_data, sd);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
const String dst_path = export_data->assets_directory + String("/") + simplified_path.trim_prefix("res://");
|
||||
print_verbose("Saving project files from " + simplified_path + " into " + dst_path);
|
||||
err = store_file_at_path(dst_path, enc_data);
|
||||
|
||||
export_data->pd.file_ofs.push_back(sd);
|
||||
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 &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames) {
|
||||
print_verbose("Creating strings resources for supported locales for project " + p_project_name);
|
||||
// Stores the string into the default values directory.
|
||||
String processed_default_xml_string = vformat(GODOT_PROJECT_NAME_XML_STRING, _android_xml_escape(p_project_name));
|
||||
store_string_at_path(p_gradle_build_dir.path_join("res/values/godot_project_name_string.xml"), processed_default_xml_string);
|
||||
|
||||
// Searches the Gradle project res/ directory to find all supported locales
|
||||
Ref<DirAccess> da = DirAccess::open(p_gradle_build_dir.path_join("res"));
|
||||
if (da.is_null()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_error("Unable to open Android resources directory.");
|
||||
}
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
da->list_dir_begin();
|
||||
while (true) {
|
||||
String file = da->get_next();
|
||||
if (file.is_empty()) {
|
||||
break;
|
||||
}
|
||||
if (!file.begins_with("values-")) {
|
||||
// NOTE: This assumes all directories that start with "values-" are for localization.
|
||||
continue;
|
||||
}
|
||||
String locale = file.replace("values-", "").replace("-r", "_");
|
||||
String locale_directory = p_gradle_build_dir.path_join("res/" + file + "/godot_project_name_string.xml");
|
||||
if (p_appnames.has(locale)) {
|
||||
String locale_project_name = p_appnames[locale];
|
||||
String processed_xml_string = vformat(GODOT_PROJECT_NAME_XML_STRING, _android_xml_escape(locale_project_name));
|
||||
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
|
||||
store_string_at_path(locale_directory, processed_xml_string);
|
||||
} else {
|
||||
// TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch
|
||||
store_string_at_path(locale_directory, processed_default_xml_string);
|
||||
}
|
||||
}
|
||||
da->list_dir_end();
|
||||
return OK;
|
||||
}
|
||||
|
||||
String bool_to_string(bool v) {
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
|
||||
String _get_gles_tag() {
|
||||
return " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n";
|
||||
}
|
||||
|
||||
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
|
||||
String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\"";
|
||||
String sizes[] = { "small", "normal", "large", "xlarge" };
|
||||
constexpr size_t num_sizes = std::size(sizes);
|
||||
for (size_t i = 0; i < num_sizes; i++) {
|
||||
String feature_name = vformat("screen/support_%s", sizes[i]);
|
||||
String feature_support = bool_to_string(p_preset->get(feature_name));
|
||||
String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support);
|
||||
manifest_screen_sizes += xml_entry;
|
||||
}
|
||||
manifest_screen_sizes += " />\n";
|
||||
return manifest_screen_sizes;
|
||||
}
|
||||
|
||||
String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug) {
|
||||
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(p_export_platform->get_project_setting(p_preset, "display/window/handheld/orientation"))));
|
||||
String manifest_activity_text = vformat(
|
||||
" <activity android:name=\".GodotApp\" "
|
||||
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
|
||||
"tools:node=\"mergeOnlyAttributes\" "
|
||||
"android:excludeFromRecents=\"%s\" "
|
||||
"android:screenOrientation=\"%s\" "
|
||||
"android:resizeableActivity=\"%s\">\n",
|
||||
bool_to_string(p_preset->get("package/exclude_from_recents")),
|
||||
orientation,
|
||||
bool_to_string(bool(p_export_platform->get_project_setting(p_preset, "display/window/size/resizable"))));
|
||||
|
||||
manifest_activity_text += " <intent-filter>\n"
|
||||
" <action android:name=\"android.intent.action.MAIN\" />\n"
|
||||
" <category android:name=\"android.intent.category.DEFAULT\" />\n";
|
||||
|
||||
bool show_in_app_library = p_preset->get("package/show_in_app_library");
|
||||
if (show_in_app_library) {
|
||||
manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
|
||||
}
|
||||
|
||||
bool uses_leanback_category = p_preset->get("package/show_in_android_tv");
|
||||
if (uses_leanback_category) {
|
||||
manifest_activity_text += " <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n";
|
||||
}
|
||||
|
||||
bool uses_home_category = p_preset->get("package/show_as_launcher_app");
|
||||
if (uses_home_category) {
|
||||
manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n";
|
||||
}
|
||||
|
||||
manifest_activity_text += " </intent-filter>\n";
|
||||
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (export_plugins[i]->supports_platform(p_export_platform)) {
|
||||
const String contents = export_plugins[i]->get_android_manifest_activity_element_contents(p_export_platform, p_debug);
|
||||
if (!contents.is_empty()) {
|
||||
manifest_activity_text += contents;
|
||||
manifest_activity_text += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest_activity_text += " </activity>\n";
|
||||
return manifest_activity_text;
|
||||
}
|
||||
|
||||
String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug, const Vector<MetadataInfo> &p_metadata) {
|
||||
int app_category_index = (int)(p_preset->get("package/app_category"));
|
||||
bool is_game = app_category_index == APP_CATEGORY_GAME;
|
||||
|
||||
String manifest_application_text = vformat(
|
||||
" <application android:label=\"@string/godot_project_name_string\"\n"
|
||||
" android:allowBackup=\"%s\"\n"
|
||||
" android:icon=\"@mipmap/icon\"\n"
|
||||
" android:isGame=\"%s\"\n"
|
||||
" android:hasFragileUserData=\"%s\"\n"
|
||||
" android:requestLegacyExternalStorage=\"%s\"\n",
|
||||
bool_to_string(p_preset->get("user_data_backup/allow")),
|
||||
bool_to_string(is_game),
|
||||
bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
|
||||
bool_to_string(p_has_read_write_storage_permission));
|
||||
if (app_category_index != APP_CATEGORY_UNDEFINED) {
|
||||
manifest_application_text += vformat(" android:appCategory=\"%s\"\n", _get_app_category_label(app_category_index));
|
||||
manifest_application_text += " tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n";
|
||||
} else {
|
||||
manifest_application_text += " tools:remove=\"android:appCategory\"\n";
|
||||
manifest_application_text += " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n";
|
||||
}
|
||||
manifest_application_text += " tools:ignore=\"GoogleAppIndexingWarning\">\n\n";
|
||||
|
||||
for (int i = 0; i < p_metadata.size(); i++) {
|
||||
manifest_application_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"%s\" android:value=\"%s\" />\n", p_metadata[i].name, p_metadata[i].value);
|
||||
}
|
||||
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (export_plugins[i]->supports_platform(p_export_platform)) {
|
||||
const String contents = export_plugins[i]->get_android_manifest_application_element_contents(p_export_platform, p_debug);
|
||||
if (!contents.is_empty()) {
|
||||
manifest_application_text += contents;
|
||||
manifest_application_text += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest_application_text += _get_activity_tag(p_export_platform, p_preset, p_debug);
|
||||
manifest_application_text += " </application>\n";
|
||||
return manifest_application_text;
|
||||
}
|
||||
|
||||
Error _store_temp_file(const String &p_simplified_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd) {
|
||||
Error err = OK;
|
||||
Ref<FileAccess> ftmp = FileAccess::create_temp(FileAccess::WRITE_READ, "export", "tmp", false, &err);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
r_sd.path_utf8 = p_simplified_path.trim_prefix("res://").utf8();
|
||||
r_sd.ofs = 0;
|
||||
r_sd.size = p_data.size();
|
||||
err = EditorExportPlatform::_encrypt_and_store_data(ftmp, p_simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, r_sd.encrypted);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
r_enc_data.resize(ftmp->get_length());
|
||||
ftmp->seek(0);
|
||||
ftmp->get_buffer(r_enc_data.ptrw(), r_enc_data.size());
|
||||
ftmp.unref();
|
||||
|
||||
// Store MD5 of original file.
|
||||
{
|
||||
unsigned char hash[16];
|
||||
CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
|
||||
r_sd.md5.resize(16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
r_sd.md5.write[i] = hash[i];
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
118
platform/android/export/gradle_export_util.h
Normal file
118
platform/android/export/gradle_export_util.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/**************************************************************************/
|
||||
/* gradle_export_util.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
||||
const String GODOT_PROJECT_NAME_XML_STRING = R"(<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">%s</string>
|
||||
</resources>
|
||||
)";
|
||||
|
||||
// Application category.
|
||||
// See https://developer.android.com/guide/topics/manifest/application-element#appCategory for standards
|
||||
static const int APP_CATEGORY_ACCESSIBILITY = 0;
|
||||
static const int APP_CATEGORY_AUDIO = 1;
|
||||
static const int APP_CATEGORY_GAME = 2;
|
||||
static const int APP_CATEGORY_IMAGE = 3;
|
||||
static const int APP_CATEGORY_MAPS = 4;
|
||||
static const int APP_CATEGORY_NEWS = 5;
|
||||
static const int APP_CATEGORY_PRODUCTIVITY = 6;
|
||||
static const int APP_CATEGORY_SOCIAL = 7;
|
||||
static const int APP_CATEGORY_VIDEO = 8;
|
||||
static const int APP_CATEGORY_UNDEFINED = 9;
|
||||
|
||||
// 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;
|
||||
|
||||
struct CustomExportData {
|
||||
EditorExportPlatform::PackData pd;
|
||||
String assets_directory;
|
||||
String libs_directory;
|
||||
bool debug;
|
||||
Vector<String> libs;
|
||||
};
|
||||
|
||||
struct MetadataInfo {
|
||||
String name;
|
||||
String value;
|
||||
};
|
||||
|
||||
int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation);
|
||||
|
||||
String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation);
|
||||
|
||||
int _get_app_category_value(int category_index);
|
||||
|
||||
String _get_app_category_label(int category_index);
|
||||
|
||||
Error _store_temp_file(const String &p_simplified_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd);
|
||||
|
||||
// Utility method used to create a directory.
|
||||
Error create_directory(const String &p_dir);
|
||||
|
||||
// Writes p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data);
|
||||
|
||||
// Writes string p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_string_at_path(const String &p_path, const String &p_data);
|
||||
|
||||
// Implementation of EditorExportSaveFunction.
|
||||
// This method will only be called as an input to export_project_files.
|
||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||
// It's functionality mirrors that of the method save_apk_file.
|
||||
// This method will be called ONLY when gradle build is enabled.
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
|
||||
// Creates strings.xml files inside the gradle project for different locales.
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames);
|
||||
|
||||
String bool_to_string(bool v);
|
||||
|
||||
String _get_gles_tag();
|
||||
|
||||
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug);
|
||||
|
||||
String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug, const Vector<MetadataInfo> &p_metadata);
|
1
platform/android/export/logo.svg
Normal file
1
platform/android/export/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path fill="#56d881" d="M22.904 20.192a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m-13.808 0a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m14.256-7.525 2.496-4.323a.52.52 0 1 0-.899-.52l-2.528 4.378a15.69 15.69 0 0 0-12.842 0L7.051 7.823a.52.52 0 1 0-.9.521l2.497 4.323C4.361 15 1.43 19.34 1 24.467h30c-.43-5.128-3.361-9.468-7.648-11.8"/></svg>
|
After Width: | Height: | Size: 422 B |
1
platform/android/export/run_icon.svg
Normal file
1
platform/android/export/run_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="M11.187 9.936a.577.577 0 1 1 .578-.578.577.577 0 0 1-.578.578m-6.374 0a.577.577 0 1 1 .577-.578.577.577 0 0 1-.577.578m6.581-3.475 1.153-1.996a.24.24 0 1 0-.415-.24l-1.167 2.021a7.244 7.244 0 0 0-5.93 0L3.868 4.225a.24.24 0 1 0-.415.24l1.153 1.996a6.806 6.806 0 0 0-3.532 5.448h13.852a6.807 6.807 0 0 0-3.532-5.448"/></svg>
|
After Width: | Height: | Size: 432 B |
188
platform/android/file_access_android.cpp
Normal file
188
platform/android/file_access_android.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/**************************************************************************/
|
||||
/* file_access_android.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "file_access_android.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
#include <android/asset_manager_jni.h>
|
||||
|
||||
AAssetManager *FileAccessAndroid::asset_manager = nullptr;
|
||||
jobject FileAccessAndroid::j_asset_manager = nullptr;
|
||||
|
||||
String FileAccessAndroid::get_path() const {
|
||||
return path_src;
|
||||
}
|
||||
|
||||
String FileAccessAndroid::get_path_absolute() const {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
Error FileAccessAndroid::open_internal(const String &p_path, int p_mode_flags) {
|
||||
_close();
|
||||
|
||||
path_src = p_path;
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
absolute_path = path;
|
||||
if (path.begins_with("/")) {
|
||||
path = path.substr(1);
|
||||
} else if (path.begins_with("res://")) {
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, ERR_UNAVAILABLE); //can't write on android..
|
||||
asset = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
|
||||
if (!asset) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
len = AAsset_getLength(asset);
|
||||
pos = 0;
|
||||
eof = false;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void FileAccessAndroid::_close() {
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
AAsset_close(asset);
|
||||
asset = nullptr;
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::is_open() const {
|
||||
return asset != nullptr;
|
||||
}
|
||||
|
||||
void FileAccessAndroid::seek(uint64_t p_position) {
|
||||
ERR_FAIL_NULL(asset);
|
||||
|
||||
AAsset_seek(asset, p_position, SEEK_SET);
|
||||
pos = p_position;
|
||||
if (pos > len) {
|
||||
pos = len;
|
||||
eof = true;
|
||||
} else {
|
||||
eof = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessAndroid::seek_end(int64_t p_position) {
|
||||
ERR_FAIL_NULL(asset);
|
||||
AAsset_seek(asset, p_position, SEEK_END);
|
||||
pos = len + p_position;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_position() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_length() const {
|
||||
return len;
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::eof_reached() const {
|
||||
return eof;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
|
||||
int r = AAsset_read(asset, p_dst, p_length);
|
||||
|
||||
if (pos + p_length > len) {
|
||||
eof = true;
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
pos += r;
|
||||
if (pos > len) {
|
||||
pos = len;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int64_t FileAccessAndroid::_get_size(const String &p_file) {
|
||||
return AAsset_getLength64(asset);
|
||||
}
|
||||
|
||||
Error FileAccessAndroid::get_error() const {
|
||||
return eof ? ERR_FILE_EOF : OK; // not sure what else it may happen
|
||||
}
|
||||
|
||||
void FileAccessAndroid::flush() {
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::file_exists(const String &p_path) {
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
if (path.begins_with("/")) {
|
||||
path = path.substr(1);
|
||||
} else if (path.begins_with("res://")) {
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
AAsset *at = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
|
||||
|
||||
if (!at) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AAsset_close(at);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileAccessAndroid::close() {
|
||||
_close();
|
||||
}
|
||||
|
||||
FileAccessAndroid::~FileAccessAndroid() {
|
||||
_close();
|
||||
}
|
||||
|
||||
void FileAccessAndroid::setup(jobject p_asset_manager) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
j_asset_manager = env->NewGlobalRef(p_asset_manager);
|
||||
asset_manager = AAssetManager_fromJava(env, j_asset_manager);
|
||||
}
|
||||
|
||||
void FileAccessAndroid::terminate() {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
env->DeleteGlobalRef(j_asset_manager);
|
||||
}
|
98
platform/android/file_access_android.h
Normal file
98
platform/android/file_access_android.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**************************************************************************/
|
||||
/* file_access_android.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <cstdio>
|
||||
|
||||
class FileAccessAndroid : public FileAccess {
|
||||
GDSOFTCLASS(FileAccessAndroid, FileAccess);
|
||||
static AAssetManager *asset_manager;
|
||||
static jobject j_asset_manager;
|
||||
|
||||
mutable AAsset *asset = nullptr;
|
||||
mutable uint64_t len = 0;
|
||||
mutable uint64_t pos = 0;
|
||||
mutable bool eof = false;
|
||||
String absolute_path;
|
||||
String path_src;
|
||||
|
||||
void _close();
|
||||
|
||||
public:
|
||||
virtual Error open_internal(const String &p_path, int p_mode_flags) override; // open a file
|
||||
virtual bool is_open() const override; // true when file is open
|
||||
|
||||
/// returns the path for the current open file
|
||||
virtual String get_path() const override;
|
||||
/// returns the absolute path for the current open file
|
||||
virtual String get_path_absolute() const override;
|
||||
|
||||
virtual void seek(uint64_t p_position) override; // seek to a given position
|
||||
virtual void seek_end(int64_t p_position = 0) override; // seek from the end of file
|
||||
virtual uint64_t get_position() const override; // get position in the file
|
||||
virtual uint64_t get_length() const override; // get size of the file
|
||||
|
||||
virtual bool eof_reached() const override; // reading passed EOF
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; // get last error
|
||||
|
||||
virtual void flush() override;
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
|
||||
virtual bool file_exists(const String &p_path) override; // return true if a file exists
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
|
||||
virtual uint64_t _get_access_time(const String &p_file) override { return 0; }
|
||||
virtual int64_t _get_size(const String &p_file) override;
|
||||
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
|
||||
virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
|
||||
virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
|
||||
virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual void close() override;
|
||||
|
||||
static void setup(jobject p_asset_manager);
|
||||
|
||||
static void terminate();
|
||||
|
||||
~FileAccessAndroid();
|
||||
};
|
394
platform/android/file_access_filesystem_jandroid.cpp
Normal file
394
platform/android/file_access_filesystem_jandroid.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/**************************************************************************/
|
||||
/* file_access_filesystem_jandroid.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "file_access_filesystem_jandroid.h"
|
||||
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
jobject FileAccessFilesystemJAndroid::file_access_handler = nullptr;
|
||||
jclass FileAccessFilesystemJAndroid::cls;
|
||||
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_open = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_get_size = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_seek = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_exists = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_last_modified = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_last_accessed = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_resize = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_size = nullptr;
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_path() const {
|
||||
return path_src;
|
||||
}
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_path_absolute() const {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
Error FileAccessFilesystemJAndroid::open_internal(const String &p_path, int p_mode_flags) {
|
||||
if (is_open()) {
|
||||
_close();
|
||||
}
|
||||
|
||||
if (_file_open) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int res = env->CallIntMethod(file_access_handler, _file_open, js, p_mode_flags);
|
||||
env->DeleteLocalRef(js);
|
||||
|
||||
if (res < 0) {
|
||||
// Errors are passed back as their negative value to differentiate from the positive file id.
|
||||
return static_cast<Error>(-res);
|
||||
}
|
||||
|
||||
id = res;
|
||||
path_src = p_path;
|
||||
absolute_path = path;
|
||||
return OK;
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::_close() {
|
||||
if (!is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_file_close) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
env->CallVoidMethod(file_access_handler, _file_close, id);
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::is_open() const {
|
||||
return id != 0;
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::seek(uint64_t p_position) {
|
||||
if (_file_seek) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_seek, id, p_position);
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::seek_end(int64_t p_position) {
|
||||
if (_file_seek_end) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_seek_end, id, p_position);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_position() const {
|
||||
if (_file_tell) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
return env->CallLongMethod(file_access_handler, _file_tell, id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_length() const {
|
||||
if (_file_get_size) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
return env->CallLongMethod(file_access_handler, _file_get_size, id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::eof_reached() const {
|
||||
if (_file_eof) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use.");
|
||||
return env->CallBooleanMethod(file_access_handler, _file_eof, id);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::_set_eof(bool eof) {
|
||||
if (_file_set_eof) {
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);
|
||||
}
|
||||
}
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_line() const {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), String(), "File must be opened before use.");
|
||||
|
||||
const size_t buffer_size_limit = 2048;
|
||||
const uint64_t file_size = get_length();
|
||||
const uint64_t start_position = get_position();
|
||||
|
||||
String result;
|
||||
LocalVector<uint8_t> line_buffer;
|
||||
size_t current_buffer_size = 0;
|
||||
uint64_t line_buffer_position = 0;
|
||||
|
||||
while (true) {
|
||||
size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
|
||||
if (line_buffer_size <= 0) {
|
||||
const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);
|
||||
break;
|
||||
}
|
||||
|
||||
current_buffer_size += line_buffer_size;
|
||||
line_buffer.resize(current_buffer_size);
|
||||
|
||||
uint64_t bytes_read = get_buffer(&line_buffer[line_buffer_position], current_buffer_size - line_buffer_position);
|
||||
if (bytes_read <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (; bytes_read > 0; line_buffer_position++, bytes_read--) {
|
||||
uint8_t elem = line_buffer[line_buffer_position];
|
||||
if (elem == '\n' || elem == '\0') {
|
||||
// Found the end of the line
|
||||
const_cast<FileAccessFilesystemJAndroid *>(this)->seek(start_position + line_buffer_position + 1);
|
||||
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
if (_file_read) {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
if (p_length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
jobject j_buffer = env->NewDirectByteBuffer(p_dst, p_length);
|
||||
int length = env->CallIntMethod(file_access_handler, _file_read, id, j_buffer);
|
||||
env->DeleteLocalRef(j_buffer);
|
||||
return length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
if (_file_write) {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use.");
|
||||
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
|
||||
if (p_length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
|
||||
jobject j_buffer = env->NewDirectByteBuffer((void *)p_src, p_length);
|
||||
bool ok = env->CallBooleanMethod(file_access_handler, _file_write, id, j_buffer);
|
||||
env->DeleteLocalRef(j_buffer);
|
||||
return ok;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Error FileAccessFilesystemJAndroid::get_error() const {
|
||||
if (eof_reached()) {
|
||||
return ERR_FILE_EOF;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error FileAccessFilesystemJAndroid::resize(int64_t p_length) {
|
||||
if (_file_resize) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, FAILED);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), FAILED, "File must be opened before use.");
|
||||
int res = env->CallIntMethod(file_access_handler, _file_resize, id, p_length);
|
||||
return static_cast<Error>(res);
|
||||
} else {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::flush() {
|
||||
if (_file_flush) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_flush, id);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) {
|
||||
if (_file_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, false);
|
||||
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(file_access_handler, _file_exists, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) {
|
||||
if (_file_last_modified) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
uint64_t result = env->CallLongMethod(file_access_handler, _file_last_modified, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::_get_access_time(const String &p_file) {
|
||||
if (_file_last_accessed) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
uint64_t result = env->CallLongMethod(file_access_handler, _file_last_accessed, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FileAccessFilesystemJAndroid::_get_size(const String &p_file) {
|
||||
if (_file_size) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, -1);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int64_t result = env->CallLongMethod(file_access_handler, _file_size, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
file_access_handler = env->NewGlobalRef(p_file_access_handler);
|
||||
|
||||
jclass c = env->GetObjectClass(file_access_handler);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_file_open = env->GetMethodID(cls, "fileOpen", "(Ljava/lang/String;I)I");
|
||||
_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
|
||||
_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
|
||||
_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
|
||||
_file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");
|
||||
_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
|
||||
_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
|
||||
_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
|
||||
_file_close = env->GetMethodID(cls, "fileClose", "(I)V");
|
||||
_file_write = env->GetMethodID(cls, "fileWrite", "(ILjava/nio/ByteBuffer;)Z");
|
||||
_file_flush = env->GetMethodID(cls, "fileFlush", "(I)V");
|
||||
_file_exists = env->GetMethodID(cls, "fileExists", "(Ljava/lang/String;)Z");
|
||||
_file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J");
|
||||
_file_last_accessed = env->GetMethodID(cls, "fileLastAccessed", "(Ljava/lang/String;)J");
|
||||
_file_resize = env->GetMethodID(cls, "fileResize", "(IJ)I");
|
||||
_file_size = env->GetMethodID(cls, "fileSize", "(Ljava/lang/String;)J");
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::terminate() {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
env->DeleteGlobalRef(cls);
|
||||
env->DeleteGlobalRef(file_access_handler);
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::close() {
|
||||
if (is_open()) {
|
||||
_close();
|
||||
}
|
||||
}
|
||||
|
||||
FileAccessFilesystemJAndroid::FileAccessFilesystemJAndroid() {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
FileAccessFilesystemJAndroid::~FileAccessFilesystemJAndroid() {
|
||||
if (is_open()) {
|
||||
_close();
|
||||
}
|
||||
}
|
111
platform/android/file_access_filesystem_jandroid.h
Normal file
111
platform/android/file_access_filesystem_jandroid.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**************************************************************************/
|
||||
/* file_access_filesystem_jandroid.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "java_godot_lib_jni.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
class FileAccessFilesystemJAndroid : public FileAccess {
|
||||
GDSOFTCLASS(FileAccessFilesystemJAndroid, FileAccess);
|
||||
static jobject file_access_handler;
|
||||
static jclass cls;
|
||||
|
||||
static jmethodID _file_open;
|
||||
static jmethodID _file_get_size;
|
||||
static jmethodID _file_seek;
|
||||
static jmethodID _file_seek_end;
|
||||
static jmethodID _file_tell;
|
||||
static jmethodID _file_eof;
|
||||
static jmethodID _file_set_eof;
|
||||
static jmethodID _file_read;
|
||||
static jmethodID _file_write;
|
||||
static jmethodID _file_flush;
|
||||
static jmethodID _file_close;
|
||||
static jmethodID _file_exists;
|
||||
static jmethodID _file_last_modified;
|
||||
static jmethodID _file_last_accessed;
|
||||
static jmethodID _file_resize;
|
||||
static jmethodID _file_size;
|
||||
|
||||
int id;
|
||||
String absolute_path;
|
||||
String path_src;
|
||||
|
||||
void _close(); ///< close a file
|
||||
void _set_eof(bool eof);
|
||||
|
||||
public:
|
||||
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||
virtual bool is_open() const override; ///< true when file is open
|
||||
|
||||
/// returns the path for the current open file
|
||||
virtual String get_path() const override;
|
||||
/// returns the absolute path for the current open file
|
||||
virtual String get_path_absolute() const override;
|
||||
|
||||
virtual void seek(uint64_t p_position) override; ///< seek to a given position
|
||||
virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file
|
||||
virtual uint64_t get_position() const override; ///< get position in the file
|
||||
virtual uint64_t get_length() const override; ///< get size of the file
|
||||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual Error resize(int64_t p_length) override;
|
||||
virtual String get_line() const override; ///< get a line
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual void flush() override;
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
|
||||
virtual bool file_exists(const String &p_path) override; ///< return true if a file exists
|
||||
|
||||
static void setup(jobject p_file_access_handler);
|
||||
static void terminate();
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file) override;
|
||||
virtual uint64_t _get_access_time(const String &p_file) override;
|
||||
virtual int64_t _get_size(const String &p_file) override;
|
||||
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
|
||||
virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
|
||||
virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
|
||||
virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual void close() override;
|
||||
|
||||
FileAccessFilesystemJAndroid();
|
||||
~FileAccessFilesystemJAndroid();
|
||||
};
|
41
platform/android/java/THIRDPARTY.md
Normal file
41
platform/android/java/THIRDPARTY.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Third-party libraries
|
||||
|
||||
This file list third-party libraries used in the Android source folder,
|
||||
with their provenance and, when relevant, modifications made to those files.
|
||||
|
||||
## com.google.android.vending.expansion.downloader
|
||||
|
||||
- Upstream: https://github.com/google/play-apk-expansion/tree/master/apkx_library
|
||||
- Version: git (9ecf54e, 2017)
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite all files under:
|
||||
|
||||
- `lib/src/com/google/android/vending/expansion/downloader`
|
||||
|
||||
Some files have been modified for yet unclear reasons.
|
||||
See the `lib/patches/com.google.android.vending.expansion.downloader.patch` file.
|
||||
|
||||
## com.google.android.vending.licensing
|
||||
|
||||
- Upstream: https://github.com/google/play-licensing/tree/master/lvl_library/
|
||||
- Version: git (eb57657, 2018) with modifications
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite all files under:
|
||||
|
||||
- `lib/aidl/com/android/vending/licensing`
|
||||
- `lib/src/com/google/android/vending/licensing`
|
||||
|
||||
Some files have been modified to silence linter errors or fix downstream issues.
|
||||
See the `lib/patches/com.google.android.vending.licensing.patch` file.
|
||||
|
||||
## com.android.apksig
|
||||
|
||||
- Upstream: https://android.googlesource.com/platform/tools/apksig/+/ac5cbb07d87cc342fcf07715857a812305d69888
|
||||
- Version: git (ac5cbb07d87cc342fcf07715857a812305d69888, 2024)
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite all files under:
|
||||
|
||||
- `editor/src/main/java/com/android/apksig`
|
54
platform/android/java/app/AndroidManifest.xml
Normal file
54
platform/android/java/app/AndroidManifest.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:installLocation="auto" >
|
||||
|
||||
<supports-screens
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00030000"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:label="@string/godot_project_name_string"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/icon"
|
||||
android:appCategory="game"
|
||||
android:isGame="true"
|
||||
android:hasFragileUserData="false"
|
||||
android:requestLegacyExternalStorage="false"
|
||||
tools:ignore="GoogleAppIndexingWarning" >
|
||||
<profileable
|
||||
android:shell="true"
|
||||
android:enabled="true"
|
||||
tools:targetApi="29" />
|
||||
|
||||
<activity
|
||||
android:name=".GodotApp"
|
||||
android:label="@string/godot_project_name_string"
|
||||
android:theme="@style/GodotAppSplashTheme"
|
||||
android:launchMode="singleInstancePerTask"
|
||||
android:excludeFromRecents="false"
|
||||
android:exported="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:resizeableActivity="false"
|
||||
tools:ignore="UnusedAttribute" >
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
10
platform/android/java/app/assetPackInstallTime/build.gradle
Normal file
10
platform/android/java/app/assetPackInstallTime/build.gradle
Normal file
@@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id 'com.android.asset-pack'
|
||||
}
|
||||
|
||||
assetPack {
|
||||
packName = "assetPackInstallTime" // Directory name for the asset pack
|
||||
dynamicDelivery {
|
||||
deliveryType = "install-time" // Delivery mode
|
||||
}
|
||||
}
|
2
platform/android/java/app/assets/.gitignore
vendored
Normal file
2
platform/android/java/app/assets/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
293
platform/android/java/app/build.gradle
Normal file
293
platform/android/java/app/build.gradle
Normal file
@@ -0,0 +1,293 @@
|
||||
// Gradle build config for Godot Engine's Android port.
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
apply from: 'config.gradle'
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
maven { url "https://central.sonatype.com/repository/maven-snapshots/"}
|
||||
|
||||
// Godot user plugins custom maven repos
|
||||
String[] mavenRepos = getGodotPluginsMavenRepos()
|
||||
if (mavenRepos != null && mavenRepos.size() > 0) {
|
||||
for (String repoUrl : mavenRepos) {
|
||||
maven {
|
||||
url repoUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
// Initializes a placeholder for the devImplementation dependency configuration.
|
||||
devImplementation {}
|
||||
// Initializes a placeholder for the monoImplementation dependency configuration.
|
||||
monoImplementation {}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
||||
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
||||
|
||||
if (rootProject.findProject(":lib")) {
|
||||
implementation project(":lib")
|
||||
} else if (rootProject.findProject(":godot:lib")) {
|
||||
implementation project(":godot:lib")
|
||||
} else {
|
||||
// Godot gradle build mode. In this scenario this project is the only one around and the Godot
|
||||
// library is available through the pre-generated godot-lib.*.aar android archive files.
|
||||
debugImplementation fileTree(dir: 'libs/debug', include: ['**/*.jar', '*.aar'])
|
||||
devImplementation fileTree(dir: 'libs/dev', include: ['**/*.jar', '*.aar'])
|
||||
releaseImplementation fileTree(dir: 'libs/release', include: ['**/*.jar', '*.aar'])
|
||||
}
|
||||
|
||||
// Godot user plugins remote dependencies
|
||||
String[] remoteDeps = getGodotPluginsRemoteBinaries()
|
||||
if (remoteDeps != null && remoteDeps.size() > 0) {
|
||||
for (String dep : remoteDeps) {
|
||||
implementation dep
|
||||
}
|
||||
}
|
||||
|
||||
// Godot user plugins local dependencies
|
||||
String[] pluginsBinaries = getGodotPluginsLocalBinaries()
|
||||
if (pluginsBinaries != null && pluginsBinaries.size() > 0) {
|
||||
implementation files(pluginsBinaries)
|
||||
}
|
||||
|
||||
// Automatically pick up local dependencies in res://addons
|
||||
String addonsDirectory = getAddonsDirectory()
|
||||
if (addonsDirectory != null && !addonsDirectory.isBlank()) {
|
||||
implementation fileTree(dir: "$addonsDirectory", include: ['*.jar', '*.aar'])
|
||||
}
|
||||
|
||||
// .NET dependencies
|
||||
String jar = '../../../../modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar'
|
||||
if (file(jar).exists()) {
|
||||
monoImplementation files(jar)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
buildToolsVersion versions.buildTools
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.javaVersion
|
||||
targetCompatibility versions.javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
assetPacks = [":assetPackInstallTime"]
|
||||
|
||||
namespace = 'com.godot.game'
|
||||
|
||||
defaultConfig {
|
||||
// The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects.
|
||||
aaptOptions {
|
||||
ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
|
||||
}
|
||||
|
||||
ndk {
|
||||
debugSymbolLevel 'NONE'
|
||||
String[] export_abi_list = getExportEnabledABIs()
|
||||
abiFilters export_abi_list
|
||||
}
|
||||
|
||||
// Feel free to modify the application id to your own.
|
||||
applicationId getExportPackageName()
|
||||
versionCode getExportVersionCode()
|
||||
versionName getExportVersionName()
|
||||
minSdkVersion getExportMinSdkVersion()
|
||||
targetSdkVersion getExportTargetSdkVersion()
|
||||
|
||||
missingDimensionStrategy 'products', 'template'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
disable 'MissingTranslation', 'UnusedResources'
|
||||
}
|
||||
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/NOTICE'
|
||||
|
||||
// Debug symbols are kept for development within Android Studio.
|
||||
if (shouldNotStrip()) {
|
||||
jniLibs {
|
||||
keepDebugSymbols += '**/*.so'
|
||||
}
|
||||
}
|
||||
|
||||
jniLibs {
|
||||
// Setting this to true causes AGP to package compressed native libraries when building the app
|
||||
// For more background, see:
|
||||
// - https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs
|
||||
// - https://stackoverflow.com/a/44704840
|
||||
useLegacyPackaging shouldUseLegacyPackaging()
|
||||
}
|
||||
|
||||
// Always select Godot's version of libc++_shared.so in case deps have their own
|
||||
pickFirst 'lib/x86/libc++_shared.so'
|
||||
pickFirst 'lib/x86_64/libc++_shared.so'
|
||||
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
|
||||
pickFirst 'lib/arm64-v8a/libc++_shared.so'
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
if (hasCustomDebugKeystore()) {
|
||||
storeFile new File(getDebugKeystoreFile())
|
||||
storePassword getDebugKeystorePassword()
|
||||
keyAlias getDebugKeyAlias()
|
||||
keyPassword getDebugKeystorePassword()
|
||||
}
|
||||
}
|
||||
|
||||
release {
|
||||
File keystoreFile = new File(getReleaseKeystoreFile())
|
||||
if (keystoreFile.isFile()) {
|
||||
storeFile keystoreFile
|
||||
storePassword getReleaseKeystorePassword()
|
||||
keyAlias getReleaseKeyAlias()
|
||||
keyPassword getReleaseKeystorePassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
debug {
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for Godot gradle builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.debug
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
|
||||
dev {
|
||||
initWith debug
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for Godot gradle builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.debug
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
|
||||
release {
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for Godot gradle builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.release
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions 'edition'
|
||||
|
||||
productFlavors {
|
||||
standard {
|
||||
getIsDefault().set(true)
|
||||
}
|
||||
mono {}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
aidl.srcDirs = ['aidl']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
|
||||
dev.jniLibs.srcDirs = ['libs/dev']
|
||||
release.jniLibs.srcDirs = ['libs/release']
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
String filenameSuffix = variant.flavorName == "mono" ? variant.name : variant.buildType.name
|
||||
output.outputFileName = "android_${filenameSuffix}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyAndRenameBinary(type: Copy) {
|
||||
// The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
|
||||
// and directories. Otherwise this check may cause permissions access failures on Windows
|
||||
// machines.
|
||||
doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
|
||||
|
||||
String exportPath = getExportPath()
|
||||
String exportFilename = getExportFilename()
|
||||
String exportEdition = getExportEdition()
|
||||
String exportBuildType = getExportBuildType()
|
||||
String exportBuildTypeCapitalized = exportBuildType.capitalize()
|
||||
String exportFormat = getExportFormat()
|
||||
|
||||
boolean isAab = exportFormat == "aab"
|
||||
boolean isMono = exportEdition == "mono"
|
||||
String filenameSuffix = isAab ? "${exportEdition}-${exportBuildType}" : exportBuildType
|
||||
if (isMono) {
|
||||
filenameSuffix = isAab ? "${exportEdition}-${exportBuildType}" : "${exportEdition}${exportBuildTypeCapitalized}"
|
||||
}
|
||||
|
||||
String sourceFilename = isAab ? "build-${filenameSuffix}.aab" : "android_${filenameSuffix}.apk"
|
||||
String sourceFilepath = isAab ? "$buildDir/outputs/bundle/${exportEdition}${exportBuildTypeCapitalized}/$sourceFilename" : "$buildDir/outputs/apk/$exportEdition/$exportBuildType/$sourceFilename"
|
||||
|
||||
from sourceFilepath
|
||||
into exportPath
|
||||
rename sourceFilename, exportFilename
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to validate the version of the Java SDK used for the Godot gradle builds.
|
||||
*/
|
||||
task validateJavaVersion {
|
||||
if (!JavaVersion.current().isCompatibleWith(versions.javaVersion)) {
|
||||
throw new GradleException("Invalid Java version ${JavaVersion.current()}. Version ${versions.javaVersion} is the minimum supported Java version for Godot gradle builds.")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
When they're scheduled to run, the copy*AARToAppModule tasks generate dependencies for the 'app'
|
||||
module, so we're ensuring the ':app:preBuild' task is set to run after those tasks.
|
||||
*/
|
||||
if (rootProject.tasks.findByPath("copyDebugAARToAppModule") != null) {
|
||||
preBuild.mustRunAfter(rootProject.tasks.named("copyDebugAARToAppModule"))
|
||||
}
|
||||
if (rootProject.tasks.findByPath("copyDevAARToAppModule") != null) {
|
||||
preBuild.mustRunAfter(rootProject.tasks.named("copyDevAARToAppModule"))
|
||||
}
|
||||
if (rootProject.tasks.findByPath("copyReleaseAARToAppModule") != null) {
|
||||
preBuild.mustRunAfter(rootProject.tasks.named("copyReleaseAARToAppModule"))
|
||||
}
|
404
platform/android/java/app/config.gradle
Normal file
404
platform/android/java/app/config.gradle
Normal file
@@ -0,0 +1,404 @@
|
||||
ext.versions = [
|
||||
androidGradlePlugin: '8.6.1',
|
||||
compileSdk : 35,
|
||||
// Also update:
|
||||
// - 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
|
||||
// - 'platform/android/detect.py#get_min_target_api()'
|
||||
minSdk : 24,
|
||||
// Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
|
||||
targetSdk : 35,
|
||||
buildTools : '35.0.0',
|
||||
kotlinVersion : '2.1.20',
|
||||
fragmentVersion : '1.8.6',
|
||||
nexusPublishVersion: '1.3.0',
|
||||
javaVersion : JavaVersion.VERSION_17,
|
||||
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
|
||||
ndkVersion : '28.1.13356709',
|
||||
splashscreenVersion: '1.0.1',
|
||||
openxrVendorsVersion: '4.1.1-stable'
|
||||
|
||||
]
|
||||
|
||||
ext.getExportPackageName = { ->
|
||||
// Retrieve the app id from the project property set by the Godot build command.
|
||||
String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : ""
|
||||
// Check if the app id is valid, otherwise use the default.
|
||||
if (appId == null || appId.isEmpty()) {
|
||||
appId = "com.godot.game"
|
||||
}
|
||||
return appId
|
||||
}
|
||||
|
||||
ext.getExportVersionCode = { ->
|
||||
String versionCode = project.hasProperty("export_version_code") ? project.property("export_version_code") : ""
|
||||
if (versionCode == null || versionCode.isEmpty()) {
|
||||
versionCode = "1"
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(versionCode)
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
ext.getExportVersionName = { ->
|
||||
String versionName = project.hasProperty("export_version_name") ? project.property("export_version_name") : ""
|
||||
if (versionName == null || versionName.isEmpty()) {
|
||||
versionName = "1.0"
|
||||
}
|
||||
return versionName
|
||||
}
|
||||
|
||||
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.getGodotLibraryVersionCode = { ->
|
||||
String versionName = ""
|
||||
int versionCode = 1
|
||||
(versionName, versionCode) = getGodotLibraryVersion()
|
||||
return versionCode
|
||||
}
|
||||
|
||||
ext.getGodotLibraryVersionName = { ->
|
||||
String versionName = ""
|
||||
int versionCode = 1
|
||||
(versionName, versionCode) = getGodotLibraryVersion()
|
||||
return versionName
|
||||
}
|
||||
|
||||
ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
|
||||
// Attempt to read the version from the `version.py` file.
|
||||
String libraryVersionName = ""
|
||||
int libraryVersionCode = 0
|
||||
|
||||
File versionFile = new File("../../../version.py")
|
||||
if (versionFile.isFile()) {
|
||||
def map = [:]
|
||||
|
||||
List<String> lines = versionFile.readLines()
|
||||
for (String line in lines) {
|
||||
String[] keyValue = line.split("=")
|
||||
String key = keyValue[0].trim()
|
||||
String value = keyValue[1].trim().replaceAll("\"", "")
|
||||
|
||||
if (requiredKeys.contains(key)) {
|
||||
if (!value.isEmpty()) {
|
||||
map[key] = value
|
||||
}
|
||||
requiredKeys.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredKeys.empty) {
|
||||
libraryVersionName = map.values().join(".")
|
||||
try {
|
||||
if (map.containsKey("status")) {
|
||||
int statusCode = 0
|
||||
String statusValue = map["status"]
|
||||
if (statusValue == null) {
|
||||
statusCode = 0
|
||||
} else if (statusValue.startsWith("dev")) {
|
||||
statusCode = 1
|
||||
} else if (statusValue.startsWith("alpha")) {
|
||||
statusCode = 2
|
||||
} else if (statusValue.startsWith("beta")) {
|
||||
statusCode = 3
|
||||
} else if (statusValue.startsWith("rc")) {
|
||||
statusCode = 4
|
||||
} else if (statusValue.startsWith("stable")) {
|
||||
statusCode = 5
|
||||
} else {
|
||||
statusCode = 0
|
||||
}
|
||||
|
||||
libraryVersionCode = statusCode
|
||||
}
|
||||
|
||||
if (map.containsKey("patch")) {
|
||||
libraryVersionCode += Integer.parseInt(map["patch"]) * 10
|
||||
}
|
||||
|
||||
if (map.containsKey("minor")) {
|
||||
libraryVersionCode += (Integer.parseInt(map["minor"]) * 1000)
|
||||
}
|
||||
|
||||
if (map.containsKey("major")) {
|
||||
libraryVersionCode += (Integer.parseInt(map["major"]) * 100000)
|
||||
}
|
||||
} catch (NumberFormatException ignore) {
|
||||
libraryVersionCode = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (libraryVersionName.isEmpty()) {
|
||||
// Fallback value in case we're unable to read the file.
|
||||
libraryVersionName = "custom_build"
|
||||
}
|
||||
|
||||
if (libraryVersionCode == 0) {
|
||||
libraryVersionCode = 1
|
||||
}
|
||||
|
||||
return [libraryVersionName, libraryVersionCode]
|
||||
}
|
||||
|
||||
ext.getGodotLibraryVersion = { ->
|
||||
List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
|
||||
return generateGodotLibraryVersion(requiredKeys)
|
||||
}
|
||||
|
||||
ext.getGodotPublishVersion = { ->
|
||||
List<String> requiredKeys = ["major", "minor", "patch", "status"]
|
||||
String versionName = ""
|
||||
int versionCode = 1
|
||||
(versionName, versionCode) = generateGodotLibraryVersion(requiredKeys)
|
||||
if (!versionName.endsWith("stable")) {
|
||||
versionName += "-SNAPSHOT"
|
||||
}
|
||||
return versionName
|
||||
}
|
||||
|
||||
final String VALUE_SEPARATOR_REGEX = "\\|"
|
||||
|
||||
// get the list of ABIs the project should be exported to
|
||||
ext.getExportEnabledABIs = { ->
|
||||
String enabledABIs = project.hasProperty("export_enabled_abis") ? project.property("export_enabled_abis") : ""
|
||||
if (enabledABIs == null || enabledABIs.isEmpty()) {
|
||||
enabledABIs = "armeabi-v7a|arm64-v8a|x86|x86_64|"
|
||||
}
|
||||
Set<String> exportAbiFilter = []
|
||||
for (String abi_name : enabledABIs.split(VALUE_SEPARATOR_REGEX)) {
|
||||
if (!abi_name.trim().isEmpty()){
|
||||
exportAbiFilter.add(abi_name)
|
||||
}
|
||||
}
|
||||
return exportAbiFilter
|
||||
}
|
||||
|
||||
ext.getExportPath = {
|
||||
String exportPath = project.hasProperty("export_path") ? project.property("export_path") : ""
|
||||
if (exportPath == null || exportPath.isEmpty()) {
|
||||
exportPath = "."
|
||||
}
|
||||
return exportPath
|
||||
}
|
||||
|
||||
ext.getExportFilename = {
|
||||
String exportFilename = project.hasProperty("export_filename") ? project.property("export_filename") : ""
|
||||
if (exportFilename == null || exportFilename.isEmpty()) {
|
||||
exportFilename = "godot_android"
|
||||
}
|
||||
return exportFilename
|
||||
}
|
||||
|
||||
ext.getExportEdition = {
|
||||
String exportEdition = project.hasProperty("export_edition") ? project.property("export_edition") : ""
|
||||
if (exportEdition == null || exportEdition.isEmpty()) {
|
||||
exportEdition = "standard"
|
||||
}
|
||||
return exportEdition
|
||||
}
|
||||
|
||||
ext.getExportBuildType = {
|
||||
String exportBuildType = project.hasProperty("export_build_type") ? project.property("export_build_type") : ""
|
||||
if (exportBuildType == null || exportBuildType.isEmpty()) {
|
||||
exportBuildType = "debug"
|
||||
}
|
||||
return exportBuildType
|
||||
}
|
||||
|
||||
ext.getExportFormat = {
|
||||
String exportFormat = project.hasProperty("export_format") ? project.property("export_format") : ""
|
||||
if (exportFormat == null || exportFormat.isEmpty()) {
|
||||
exportFormat = "apk"
|
||||
}
|
||||
return exportFormat
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_maven_repos' property and return the list
|
||||
* of maven repos.
|
||||
*/
|
||||
ext.getGodotPluginsMavenRepos = { ->
|
||||
Set<String> mavenRepos = []
|
||||
|
||||
// Retrieve the list of maven repos.
|
||||
if (project.hasProperty("plugins_maven_repos")) {
|
||||
String mavenReposProperty = project.property("plugins_maven_repos")
|
||||
if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) {
|
||||
for (String mavenRepoUrl : mavenReposProperty.split(VALUE_SEPARATOR_REGEX)) {
|
||||
mavenRepos += mavenRepoUrl.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mavenRepos
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_remote_binaries' property and return
|
||||
* it for inclusion in the build dependencies.
|
||||
*/
|
||||
ext.getGodotPluginsRemoteBinaries = { ->
|
||||
Set<String> remoteDeps = []
|
||||
|
||||
// Retrieve the list of remote plugins binaries.
|
||||
if (project.hasProperty("plugins_remote_binaries")) {
|
||||
String remoteDepsList = project.property("plugins_remote_binaries")
|
||||
if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) {
|
||||
for (String dep: remoteDepsList.split(VALUE_SEPARATOR_REGEX)) {
|
||||
remoteDeps += dep.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
return remoteDeps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_local_binaries' property and return
|
||||
* their binaries for inclusion in the build dependencies.
|
||||
*/
|
||||
ext.getGodotPluginsLocalBinaries = { ->
|
||||
Set<String> binDeps = []
|
||||
|
||||
// Retrieve the list of local plugins binaries.
|
||||
if (project.hasProperty("plugins_local_binaries")) {
|
||||
String pluginsList = project.property("plugins_local_binaries")
|
||||
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
|
||||
for (String plugin : pluginsList.split(VALUE_SEPARATOR_REGEX)) {
|
||||
binDeps += plugin.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binDeps
|
||||
}
|
||||
|
||||
ext.getDebugKeystoreFile = { ->
|
||||
String keystoreFile = project.hasProperty("debug_keystore_file") ? project.property("debug_keystore_file") : ""
|
||||
if (keystoreFile == null || keystoreFile.isEmpty()) {
|
||||
keystoreFile = "."
|
||||
}
|
||||
return keystoreFile
|
||||
}
|
||||
|
||||
ext.hasCustomDebugKeystore = { ->
|
||||
File keystoreFile = new File(getDebugKeystoreFile())
|
||||
return keystoreFile.isFile()
|
||||
}
|
||||
|
||||
ext.getDebugKeystorePassword = { ->
|
||||
String keystorePassword = project.hasProperty("debug_keystore_password") ? project.property("debug_keystore_password") : ""
|
||||
if (keystorePassword == null || keystorePassword.isEmpty()) {
|
||||
keystorePassword = "android"
|
||||
}
|
||||
return keystorePassword
|
||||
}
|
||||
|
||||
ext.getDebugKeyAlias = { ->
|
||||
String keyAlias = project.hasProperty("debug_keystore_alias") ? project.property("debug_keystore_alias") : ""
|
||||
if (keyAlias == null || keyAlias.isEmpty()) {
|
||||
keyAlias = "androiddebugkey"
|
||||
}
|
||||
return keyAlias
|
||||
}
|
||||
|
||||
ext.getReleaseKeystoreFile = { ->
|
||||
String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""
|
||||
if (keystoreFile == null || keystoreFile.isEmpty()) {
|
||||
keystoreFile = "."
|
||||
}
|
||||
return keystoreFile
|
||||
}
|
||||
|
||||
ext.getReleaseKeystorePassword = { ->
|
||||
String keystorePassword = project.hasProperty("release_keystore_password") ? project.property("release_keystore_password") : ""
|
||||
return keystorePassword
|
||||
}
|
||||
|
||||
ext.getReleaseKeyAlias = { ->
|
||||
String keyAlias = project.hasProperty("release_keystore_alias") ? project.property("release_keystore_alias") : ""
|
||||
return keyAlias
|
||||
}
|
||||
|
||||
ext.isAndroidStudio = { ->
|
||||
return project.hasProperty('android.injected.invoked.from.ide')
|
||||
}
|
||||
|
||||
ext.shouldZipAlign = { ->
|
||||
String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : ""
|
||||
if (zipAlignFlag == null || zipAlignFlag.isEmpty()) {
|
||||
if (isAndroidStudio()) {
|
||||
zipAlignFlag = "true"
|
||||
} else {
|
||||
zipAlignFlag = "false"
|
||||
}
|
||||
}
|
||||
return Boolean.parseBoolean(zipAlignFlag)
|
||||
}
|
||||
|
||||
ext.shouldSign = { ->
|
||||
String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : ""
|
||||
if (signFlag == null || signFlag.isEmpty()) {
|
||||
if (isAndroidStudio()) {
|
||||
signFlag = "true"
|
||||
} else {
|
||||
signFlag = "false"
|
||||
}
|
||||
}
|
||||
return Boolean.parseBoolean(signFlag)
|
||||
}
|
||||
|
||||
ext.shouldNotStrip = { ->
|
||||
return isAndroidStudio() || project.hasProperty("doNotStrip")
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the legacy convention of compressing all .so files in the APK.
|
||||
*
|
||||
* For more background, see:
|
||||
* - https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs
|
||||
* - https://stackoverflow.com/a/44704840
|
||||
*/
|
||||
ext.shouldUseLegacyPackaging = { ->
|
||||
String legacyPackagingFlag = project.hasProperty("compress_native_libraries") ? project.property("compress_native_libraries") : ""
|
||||
if (legacyPackagingFlag != null && !legacyPackagingFlag.isEmpty()) {
|
||||
return Boolean.parseBoolean(legacyPackagingFlag)
|
||||
}
|
||||
|
||||
if (getExportMinSdkVersion() <= 29) {
|
||||
// Use legacy packaging for compatibility with device running api <= 29.
|
||||
// See https://github.com/godotengine/godot/issues/108842 for reference.
|
||||
return true
|
||||
}
|
||||
|
||||
// Default behavior for minSdk > 29.
|
||||
return false
|
||||
}
|
||||
|
||||
ext.getAddonsDirectory = { ->
|
||||
String addonsDirectory = project.hasProperty("addons_directory") ? project.property("addons_directory") : ""
|
||||
return addonsDirectory
|
||||
}
|
28
platform/android/java/app/gradle.properties
Normal file
28
platform/android/java/app/gradle.properties
Normal file
@@ -0,0 +1,28 @@
|
||||
# Godot gradle build settings.
|
||||
# These properties apply when running a gradle build from the Godot editor.
|
||||
# NOTE: This should be kept in sync with 'godot/platform/android/java/gradle.properties' except
|
||||
# where otherwise specified.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# https://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx4536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# https://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
org.gradle.warning.mode=all
|
||||
|
||||
# Enable resource optimizations for release build.
|
||||
# NOTE: This is turned off for template release build in order to support the build legacy process.
|
||||
android.enableResourceOptimizations=true
|
||||
|
||||
# Fix gradle build errors when the build path contains non-ASCII characters
|
||||
android.overridePathCheck=true
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ar</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-bg</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ca</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-cs</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-da</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-de</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-el</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-en</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-es_ES</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-es</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-fa</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-fi</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-fr</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-hi</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-hr</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-hu</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-in</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-it</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-iw</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ja</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ko</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-lt</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-lv</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-nb</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-nl</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-pl</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-pt</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ro</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-ru</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-sk</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-sl</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-sr</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-sv</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-th</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-tl</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-tr</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-uk</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-vi</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-zh_HK</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-zh_TW</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name-zh</string>
|
||||
</resources>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="godot_project_name_string">godot-project-name</string>
|
||||
</resources>
|
18
platform/android/java/app/res/values/themes.xml
Normal file
18
platform/android/java/app/res/values/themes.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- GodotAppMainTheme is auto-generated during export. Manual changes will be overwritten.
|
||||
To add custom attributes, use the "gradle_build/custom_theme_attributes" Android export option. -->
|
||||
<style name="GodotAppMainTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
|
||||
<item name="android:windowSwipeToDismiss">false</item>
|
||||
<item name="android:windowIsTranslucent">false</item>
|
||||
</style>
|
||||
|
||||
<!-- GodotAppSplashTheme is auto-generated during export. Manual changes will be overwritten.
|
||||
To add custom attributes, use the "gradle_build/custom_theme_attributes" Android export option. -->
|
||||
<style name="GodotAppSplashTheme" parent="Theme.SplashScreen">
|
||||
<item name="android:windowSplashScreenBackground">@mipmap/icon_background</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@mipmap/icon_foreground</item>
|
||||
<item name="postSplashScreenTheme">@style/GodotAppMainTheme</item>
|
||||
<item name="android:windowIsTranslucent">false</item>
|
||||
</style>
|
||||
</resources>
|
18
platform/android/java/app/settings.gradle
Normal file
18
platform/android/java/app/settings.gradle
Normal file
@@ -0,0 +1,18 @@
|
||||
// This is the root directory of the Godot Android gradle build.
|
||||
pluginManagement {
|
||||
apply from: 'config.gradle'
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version versions.androidGradlePlugin
|
||||
id 'org.jetbrains.kotlin.android' version versions.kotlinVersion
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
maven { url "https://central.sonatype.com/repository/maven-snapshots/"}
|
||||
}
|
||||
}
|
||||
|
||||
include ':assetPackInstallTime'
|
86
platform/android/java/app/src/com/godot/game/GodotApp.java
Normal file
86
platform/android/java/app/src/com/godot/game/GodotApp.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************/
|
||||
/* GodotApp.java */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
package com.godot.game;
|
||||
|
||||
import org.godotengine.godot.Godot;
|
||||
import org.godotengine.godot.GodotActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
|
||||
/**
|
||||
* Template activity for Godot Android builds.
|
||||
* Feel free to extend and modify this class for your custom logic.
|
||||
*/
|
||||
public class GodotApp extends GodotActivity {
|
||||
static {
|
||||
// .NET libraries.
|
||||
if (BuildConfig.FLAVOR.equals("mono")) {
|
||||
try {
|
||||
Log.v("GODOT", "Loading System.Security.Cryptography.Native.Android library");
|
||||
System.loadLibrary("System.Security.Cryptography.Native.Android");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e("GODOT", "Unable to load System.Security.Cryptography.Native.Android library");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Runnable updateWindowAppearance = () -> {
|
||||
Godot godot = getGodot();
|
||||
if (godot != null) {
|
||||
godot.enableImmersiveMode(godot.isInImmersiveMode(), true);
|
||||
godot.enableEdgeToEdge(godot.isInEdgeToEdgeMode(), true);
|
||||
godot.setSystemBarsAppearance();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
SplashScreen.installSplashScreen(this);
|
||||
EdgeToEdge.enable(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateWindowAppearance.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGodotMainLoopStarted() {
|
||||
super.onGodotMainLoopStarted();
|
||||
runOnUiThread(updateWindowAppearance);
|
||||
}
|
||||
}
|
340
platform/android/java/build.gradle
Normal file
340
platform/android/java/build.gradle
Normal file
@@ -0,0 +1,340 @@
|
||||
plugins {
|
||||
id 'io.github.gradle-nexus.publish-plugin'
|
||||
}
|
||||
|
||||
apply from: 'app/config.gradle'
|
||||
apply from: 'scripts/publish-root.gradle'
|
||||
|
||||
ext {
|
||||
PUBLISH_VERSION = getGodotPublishVersion()
|
||||
}
|
||||
|
||||
group = ossrhGroupId
|
||||
version = PUBLISH_VERSION
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
maven { url "https://central.sonatype.com/repository/maven-snapshots/"}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
|
||||
supportedFlavors = ["editor", "template"]
|
||||
supportedAndroidDistributions = ["android", "horizonos", "picoos"]
|
||||
supportedFlavorsBuildTypes = [
|
||||
"editor": ["dev", "debug", "release"],
|
||||
"template": ["dev", "debug", "release"]
|
||||
]
|
||||
supportedEditions = ["standard", "mono"]
|
||||
|
||||
// Used by gradle to specify which architecture to build for by default when running
|
||||
// `./gradlew build` (this command is usually used by Android Studio).
|
||||
// If building manually on the command line, it's recommended to use the
|
||||
// `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
|
||||
// The {selectedAbis} values must be from the {supportedAbis} values.
|
||||
selectedAbis = ["arm64"]
|
||||
|
||||
rootDir = "../../.."
|
||||
binDir = "$rootDir/bin/"
|
||||
androidEditorBuildsDir = "$binDir/android_editor_builds/"
|
||||
}
|
||||
|
||||
def getSconsTaskName(String flavor, String buildType, String abi) {
|
||||
return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Godot gradle build template by zipping the source files from the app directory, as well
|
||||
* as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
|
||||
* The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
|
||||
*/
|
||||
task zipGradleBuild(type: Zip) {
|
||||
onlyIf { generateGodotTemplates.state.executed || generateGodotMonoTemplates.state.executed || generateDevTemplate.state.executed }
|
||||
doFirst {
|
||||
logger.lifecycle("Generating Godot gradle build template")
|
||||
}
|
||||
from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))
|
||||
include '**/*'
|
||||
archiveFileName = 'android_source.zip'
|
||||
destinationDirectory = file(binDir)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the scons build tasks responsible for generating the Godot native shared
|
||||
* libraries should be excluded.
|
||||
*/
|
||||
def excludeSconsBuildTasks() {
|
||||
return !isAndroidStudio() && !project.hasProperty("generateNativeLibs")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the list of build tasks that should be excluded from the build process.\
|
||||
*/
|
||||
def templateExcludedBuildTask() {
|
||||
// We exclude these gradle tasks so we can run the scons command manually.
|
||||
def excludedTasks = []
|
||||
if (excludeSconsBuildTasks()) {
|
||||
logger.info("Excluding Android studio build tasks")
|
||||
for (String flavor : supportedFlavors) {
|
||||
String[] supportedBuildTypes = supportedFlavorsBuildTypes[flavor]
|
||||
for (String buildType : supportedBuildTypes) {
|
||||
for (String abi : selectedAbis) {
|
||||
excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return excludedTasks
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the build tasks for the given flavor
|
||||
* @param flavor Must be one of the supported flavors ('template' / 'editor')
|
||||
* @param edition Must be one of the supported editions ('standard' / 'mono')
|
||||
* @param androidDistro Must be one of the supported Android distributions ('android' / 'horizonos' / 'picoos')
|
||||
*/
|
||||
def generateBuildTasks(String flavor = "template", String edition = "standard", String androidDistro = "android") {
|
||||
if (!supportedFlavors.contains(flavor)) {
|
||||
throw new GradleException("Invalid build flavor: $flavor")
|
||||
}
|
||||
if (!supportedAndroidDistributions.contains(androidDistro)) {
|
||||
throw new GradleException("Invalid Android distribution: $androidDistro")
|
||||
}
|
||||
if (!supportedEditions.contains(edition)) {
|
||||
throw new GradleException("Invalid build edition: $edition")
|
||||
}
|
||||
if (edition == "mono" && flavor != "template") {
|
||||
throw new GradleException("'mono' edition only supports the 'template' flavor.")
|
||||
}
|
||||
|
||||
String capitalizedAndroidDistro = androidDistro.capitalize()
|
||||
def buildTasks = []
|
||||
|
||||
// Only build the binary files for which we have native shared libraries unless we intend
|
||||
// to run the scons build tasks.
|
||||
boolean excludeSconsBuildTasks = excludeSconsBuildTasks()
|
||||
boolean isTemplate = flavor == "template"
|
||||
String libsDir = isTemplate ? "lib/libs/" : "lib/libs/tools/"
|
||||
for (String target : supportedFlavorsBuildTypes[flavor]) {
|
||||
File targetLibs = new File(libsDir + target)
|
||||
|
||||
String targetSuffix = target
|
||||
if (target == "dev") {
|
||||
targetSuffix = "debug.dev"
|
||||
}
|
||||
|
||||
if (!excludeSconsBuildTasks || (targetLibs != null
|
||||
&& targetLibs.isDirectory()
|
||||
&& targetLibs.listFiles() != null
|
||||
&& targetLibs.listFiles().length > 0)) {
|
||||
|
||||
String capitalizedTarget = target.capitalize()
|
||||
String capitalizedEdition = edition.capitalize()
|
||||
if (isTemplate) {
|
||||
// Copy the Godot android library archive file into the app module libs directory.
|
||||
// Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
String copyAARTaskName = "copy${capitalizedTarget}AARToAppModule"
|
||||
if (tasks.findByName(copyAARTaskName) != null) {
|
||||
buildTasks += tasks.getByName(copyAARTaskName)
|
||||
} else {
|
||||
buildTasks += tasks.create(name: copyAARTaskName, type: Copy) {
|
||||
dependsOn ":lib:assembleTemplate${capitalizedTarget}"
|
||||
from('lib/build/outputs/aar')
|
||||
include("godot-lib.template_${targetSuffix}.aar")
|
||||
into("app/libs/${target}")
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the Godot android library archive file into the root bin directory.
|
||||
// Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
String copyAARToBinTaskName = "copy${capitalizedTarget}AARToBin"
|
||||
if (tasks.findByName(copyAARToBinTaskName) != null) {
|
||||
buildTasks += tasks.getByName(copyAARToBinTaskName)
|
||||
} else {
|
||||
buildTasks += tasks.create(name: copyAARToBinTaskName, type: Copy) {
|
||||
dependsOn ":lib:assembleTemplate${capitalizedTarget}"
|
||||
from('lib/build/outputs/aar')
|
||||
include("godot-lib.template_${targetSuffix}.aar")
|
||||
into(binDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the generated binary template into the Godot bin directory.
|
||||
// Depends on the app build task to ensure the binary is generated prior to copying.
|
||||
String copyBinaryTaskName = "copy${capitalizedEdition}${capitalizedTarget}BinaryToBin"
|
||||
if (tasks.findByName(copyBinaryTaskName) != null) {
|
||||
buildTasks += tasks.getByName(copyBinaryTaskName)
|
||||
} else {
|
||||
buildTasks += tasks.create(name: copyBinaryTaskName, type: Copy) {
|
||||
String filenameSuffix = edition == "mono" ? "${edition}${capitalizedTarget}" : target
|
||||
dependsOn ":app:assemble${capitalizedEdition}${capitalizedTarget}"
|
||||
from("app/build/outputs/apk/${edition}/${target}") {
|
||||
include("android_${filenameSuffix}.apk")
|
||||
}
|
||||
into(binDir)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Copy the generated editor apk to the bin directory.
|
||||
String copyEditorApkTaskName = "copyEditor${capitalizedAndroidDistro}${capitalizedTarget}ApkToBin"
|
||||
if (tasks.findByName(copyEditorApkTaskName) != null) {
|
||||
buildTasks += tasks.getByName(copyEditorApkTaskName)
|
||||
} else {
|
||||
buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) {
|
||||
dependsOn ":editor:assemble${capitalizedAndroidDistro}${capitalizedTarget}"
|
||||
from("editor/build/outputs/apk/${androidDistro}/${target}") {
|
||||
include("android_editor-${androidDistro}-${target}*.apk")
|
||||
}
|
||||
into(androidEditorBuildsDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the generated editor aab to the bin directory.
|
||||
String copyEditorAabTaskName = "copyEditor${capitalizedAndroidDistro}${capitalizedTarget}AabToBin"
|
||||
if (tasks.findByName(copyEditorAabTaskName) != null) {
|
||||
buildTasks += tasks.getByName(copyEditorAabTaskName)
|
||||
} else {
|
||||
buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) {
|
||||
dependsOn ":editor:bundle${capitalizedAndroidDistro}${capitalizedTarget}"
|
||||
from("editor/build/outputs/bundle/${androidDistro}${capitalizedTarget}")
|
||||
into(androidEditorBuildsDir)
|
||||
include("android_editor-${androidDistro}-${target}*.aab")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.info("No native shared libs for target $target. Skipping build.")
|
||||
}
|
||||
}
|
||||
|
||||
return buildTasks
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Godot Editor binaries for Android devices.
|
||||
*
|
||||
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
||||
* must have been generated (via scons) prior to running this gradle task.
|
||||
* The task will only build the binaries for which the shared libraries is available.
|
||||
*/
|
||||
task generateGodotEditor {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = generateBuildTasks("editor", "standard", "android")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Godot Editor binaries for HorizonOS devices.
|
||||
*
|
||||
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
||||
* must have been generated (via scons) prior to running this gradle task.
|
||||
* The task will only build the binaries for which the shared libraries is available.
|
||||
*/
|
||||
task generateGodotHorizonOSEditor {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = generateBuildTasks("editor", "standard", "horizonos")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Godot Editor binaries for PicoOS devices.
|
||||
*
|
||||
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
||||
* must have been generated (via scons) prior to running this gradle task.
|
||||
* The task will only build the binaries for which the shared libraries is available.
|
||||
*/
|
||||
task generateGodotPicoOSEditor {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = generateBuildTasks("editor", "standard", "picoos")
|
||||
}
|
||||
|
||||
/**
|
||||
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
|
||||
*/
|
||||
task generateGodotTemplates {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = generateBuildTasks("template")
|
||||
|
||||
finalizedBy 'zipGradleBuild'
|
||||
}
|
||||
|
||||
/**
|
||||
* Master task used to coordinate the tasks defined above to generate the set of Godot templates
|
||||
* for the 'mono' edition of the engine.
|
||||
*/
|
||||
task generateGodotMonoTemplates {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = generateBuildTasks("template", "mono")
|
||||
|
||||
finalizedBy 'zipGradleBuild'
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
dependsOn 'cleanGodotEditor'
|
||||
dependsOn 'cleanGodotTemplates'
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the generated editor artifacts.
|
||||
*/
|
||||
task cleanGodotEditor(type: Delete) {
|
||||
// Delete the generated native tools libs
|
||||
delete("lib/libs/tools")
|
||||
|
||||
// Delete the library generated AAR files
|
||||
delete("lib/build/outputs/aar")
|
||||
|
||||
// Delete the generated binary apks
|
||||
delete("editor/build/outputs/apk")
|
||||
|
||||
// Delete the generated aab binaries
|
||||
delete("editor/build/outputs/bundle")
|
||||
|
||||
// Delete the Godot editor apks & aabs in the Godot bin directory
|
||||
delete(androidEditorBuildsDir)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the generated template artifacts.
|
||||
*/
|
||||
task cleanGodotTemplates(type: Delete) {
|
||||
// Delete the generated native libs
|
||||
delete("lib/libs")
|
||||
|
||||
// Delete the library generated AAR files
|
||||
delete("lib/build/outputs/aar")
|
||||
|
||||
// Delete the app libs directory contents
|
||||
delete("app/libs")
|
||||
|
||||
// Delete the generated binary apks
|
||||
delete("app/build/outputs/apk")
|
||||
|
||||
// Delete the Godot templates in the Godot bin directory
|
||||
delete("$binDir/android_debug.apk")
|
||||
delete("$binDir/android_dev.apk")
|
||||
delete("$binDir/android_release.apk")
|
||||
delete("$binDir/android_monoDebug.apk")
|
||||
delete("$binDir/android_monoDev.apk")
|
||||
delete("$binDir/android_monoRelease.apk")
|
||||
delete("$binDir/android_source.zip")
|
||||
delete("$binDir/godot-lib.template_debug.aar")
|
||||
delete("$binDir/godot-lib.template_debug.dev.aar")
|
||||
delete("$binDir/godot-lib.template_release.aar")
|
||||
|
||||
// Cover deletion for the libs using the previous naming scheme
|
||||
delete("$binDir/godot-lib.debug.aar")
|
||||
delete("$binDir/godot-lib.dev.aar")
|
||||
delete("$binDir/godot-lib.release.aar")
|
||||
|
||||
// Delete the native debug symbols files.
|
||||
delete("$binDir/android-editor-debug-native-symbols.zip")
|
||||
delete("$binDir/android-editor-dev-native-symbols.zip")
|
||||
delete("$binDir/android-editor-release-native-symbols.zip")
|
||||
delete("$binDir/android-template-debug-native-symbols.zip")
|
||||
delete("$binDir/android-template-dev-native-symbols.zip")
|
||||
delete("$binDir/android-template-release-native-symbols.zip")
|
||||
}
|
199
platform/android/java/editor/build.gradle
Normal file
199
platform/android/java/editor/build.gradle
Normal file
@@ -0,0 +1,199 @@
|
||||
// Gradle build config for Godot Engine's Android port.
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'base'
|
||||
}
|
||||
|
||||
ext {
|
||||
// Retrieve the build number from the environment variable; default to 0 if none is specified.
|
||||
// The build number is added as a suffix to the version code for upload to the Google Play store.
|
||||
getEditorBuildNumber = { ->
|
||||
int buildNumber = 0
|
||||
String versionStatus = System.getenv("GODOT_VERSION_STATUS")
|
||||
if (versionStatus != null && !versionStatus.isEmpty()) {
|
||||
try {
|
||||
buildNumber = Integer.parseInt(versionStatus.replaceAll("[^0-9]", ""))
|
||||
} catch (NumberFormatException ignored) {
|
||||
buildNumber = 0
|
||||
}
|
||||
}
|
||||
|
||||
return buildNumber
|
||||
}
|
||||
// Value by which the Godot version code should be offset by to make room for the build number
|
||||
editorBuildNumberOffset = 100
|
||||
|
||||
// Return the keystore file used for signing the release build.
|
||||
getGodotKeystoreFile = { ->
|
||||
def keyStore = System.getenv("GODOT_ANDROID_SIGN_KEYSTORE")
|
||||
if (keyStore == null || keyStore.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return file(keyStore)
|
||||
}
|
||||
|
||||
// Return the key alias used for signing the release build.
|
||||
getGodotKeyAlias = { ->
|
||||
def kAlias = System.getenv("GODOT_ANDROID_KEYSTORE_ALIAS")
|
||||
return kAlias
|
||||
}
|
||||
|
||||
// Return the password for the key used for signing the release build.
|
||||
getGodotSigningPassword = { ->
|
||||
def signingPassword = System.getenv("GODOT_ANDROID_SIGN_PASSWORD")
|
||||
return signingPassword
|
||||
}
|
||||
|
||||
// Returns true if the environment variables contains the configuration for signing the release
|
||||
// build.
|
||||
hasReleaseSigningConfigs = { ->
|
||||
def keystoreFile = getGodotKeystoreFile()
|
||||
def keyAlias = getGodotKeyAlias()
|
||||
def signingPassword = getGodotSigningPassword()
|
||||
|
||||
return keystoreFile != null && keystoreFile.isFile()
|
||||
&& keyAlias != null && !keyAlias.isEmpty()
|
||||
&& signingPassword != null && !signingPassword.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
def generateVersionCode() {
|
||||
int libraryVersionCode = getGodotLibraryVersionCode()
|
||||
return (libraryVersionCode * editorBuildNumberOffset) + getEditorBuildNumber()
|
||||
}
|
||||
|
||||
def generateVersionName() {
|
||||
String libraryVersionName = getGodotLibraryVersionName()
|
||||
int buildNumber = getEditorBuildNumber()
|
||||
return buildNumber == 0 ? libraryVersionName : libraryVersionName + ".$buildNumber"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
buildToolsVersion versions.buildTools
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
namespace = "org.godotengine.editor"
|
||||
|
||||
defaultConfig {
|
||||
// The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
|
||||
applicationId "org.godotengine.editor.v4"
|
||||
versionCode generateVersionCode()
|
||||
versionName generateVersionName()
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
|
||||
missingDimensionStrategy 'products', 'editor'
|
||||
manifestPlaceholders += [
|
||||
editorAppName: "Godot Engine 4",
|
||||
editorBuildSuffix: ""
|
||||
]
|
||||
|
||||
ndk { debugSymbolLevel 'NONE' }
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = "android_editor"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.javaVersion
|
||||
targetCompatibility versions.javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile getGodotKeystoreFile()
|
||||
storePassword getGodotSigningPassword()
|
||||
keyAlias getGodotKeyAlias()
|
||||
keyPassword getGodotSigningPassword()
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
dev {
|
||||
initWith debug
|
||||
applicationIdSuffix ".dev"
|
||||
manifestPlaceholders += [editorBuildSuffix: " (dev)"]
|
||||
}
|
||||
|
||||
debug {
|
||||
initWith release
|
||||
applicationIdSuffix ".debug"
|
||||
manifestPlaceholders += [editorBuildSuffix: " (debug)"]
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
|
||||
release {
|
||||
if (hasReleaseSigningConfigs()) {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
// Debug symbols are kept for development within Android Studio.
|
||||
if (shouldNotStrip()) {
|
||||
jniLibs {
|
||||
keepDebugSymbols += '**/*.so'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions = ["android_distribution"]
|
||||
productFlavors {
|
||||
android {
|
||||
dimension "android_distribution"
|
||||
missingDimensionStrategy 'products', 'editor'
|
||||
}
|
||||
horizonos {
|
||||
dimension "android_distribution"
|
||||
missingDimensionStrategy 'products', 'editor'
|
||||
ndk {
|
||||
//noinspection ChromeOsAbiSupport
|
||||
abiFilters "arm64-v8a"
|
||||
}
|
||||
applicationIdSuffix ".meta"
|
||||
versionNameSuffix "-meta"
|
||||
targetSdkVersion 32
|
||||
}
|
||||
picoos {
|
||||
dimension "android_distribution"
|
||||
missingDimensionStrategy 'products', 'editor'
|
||||
ndk {
|
||||
//noinspection ChromeOsAbiSupport
|
||||
abiFilters "arm64-v8a"
|
||||
}
|
||||
applicationIdSuffix ".pico"
|
||||
versionNameSuffix "-pico"
|
||||
minSdkVersion 29
|
||||
targetSdkVersion 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
|
||||
|
||||
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
||||
implementation project(":lib")
|
||||
|
||||
implementation "androidx.window:window:1.3.0"
|
||||
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.2.1"
|
||||
implementation "org.bouncycastle:bcprov-jdk15to18:1.78"
|
||||
|
||||
// Meta dependencies
|
||||
horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:$versions.openxrVendorsVersion"
|
||||
// Pico dependencies
|
||||
picoosImplementation "org.godotengine:godot-openxr-vendors-pico:$versions.openxrVendorsVersion"
|
||||
}
|
1
platform/android/java/editor/src/.gitignore
vendored
Normal file
1
platform/android/java/editor/src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!/debug
|
@@ -0,0 +1,38 @@
|
||||
/**************************************************************************/
|
||||
/* GodotEditor.kt */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
package org.godotengine.editor
|
||||
|
||||
/**
|
||||
* Primary window of the Godot Editor.
|
||||
*
|
||||
* This is the implementation of the editor used when running on regular Android devices.
|
||||
*/
|
||||
open class GodotEditor : BaseGodotEditor()
|
@@ -0,0 +1,91 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:horizonos="http://schemas.horizonos/sdk">
|
||||
|
||||
<horizonos:uses-horizonos-sdk
|
||||
horizonos:minSdkVersion="69"
|
||||
horizonos:targetSdkVersion="69" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.vr.headtracking"
|
||||
android:required="true"
|
||||
android:version="1"/>
|
||||
|
||||
<!-- Oculus Quest hand tracking -->
|
||||
<uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
|
||||
<uses-feature
|
||||
android:name="oculus.software.handtracking"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Passthrough feature flag -->
|
||||
<uses-feature android:name="com.oculus.feature.PASSTHROUGH"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Overlay keyboard support -->
|
||||
<uses-feature android:name="oculus.software.overlay_keyboard" android:required="false"/>
|
||||
|
||||
<!-- Render model -->
|
||||
<uses-permission android:name="com.oculus.permission.RENDER_MODEL" />
|
||||
<uses-feature android:name="com.oculus.feature.RENDER_MODEL" android:required="false" />
|
||||
|
||||
<!-- Anchor api -->
|
||||
<uses-permission android:name="com.oculus.permission.USE_ANCHOR_API" />
|
||||
|
||||
<!-- Scene api -->
|
||||
<uses-permission android:name="com.oculus.permission.USE_SCENE" />
|
||||
|
||||
<!-- Temp removal of the 'REQUEST_INSTALL_PACKAGES' permission as it's currently forbidden by the Horizon OS store -->
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove" />
|
||||
|
||||
<!-- Passthrough feature -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera2.any"
|
||||
android:required="false"/>
|
||||
<uses-permission android:name="horizonos.permission.HEADSET_CAMERA"/>
|
||||
|
||||
<application>
|
||||
|
||||
<activity
|
||||
android:name=".GodotEditor"
|
||||
android:exported="true"
|
||||
android:screenOrientation="landscape"
|
||||
tools:node="merge"
|
||||
tools:replace="android:screenOrientation">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="com.oculus.intent.category.2D" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="com.oculus.vrshell.free_resizing_lock_aspect_ratio" android:value="true"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="com.oculus.intent.category.VR" />
|
||||
<category android:name="org.khronos.openxr.intent.category.IMMERSIVE_HMD" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Supported Meta devices -->
|
||||
<meta-data
|
||||
android:name="com.oculus.supportedDevices"
|
||||
android:value="quest2|quest3|questpro"
|
||||
tools:replace="android:value" />
|
||||
|
||||
<!-- Enable system splash screen -->
|
||||
<meta-data android:name="com.oculus.ossplash" android:value="true"/>
|
||||
<!-- Enable passthrough background during the splash screen -->
|
||||
<meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
|
||||
|
||||
<meta-data android:name="com.oculus.handtracking.version" android:value="V2.0" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
platform/android/java/editor/src/horizonos/assets/vr_splash.png
Normal file
BIN
platform/android/java/editor/src/horizonos/assets/vr_splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,56 @@
|
||||
/**************************************************************************/
|
||||
/* GodotEditor.kt */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
package org.godotengine.editor
|
||||
|
||||
/**
|
||||
* Primary window of the Godot Editor.
|
||||
*
|
||||
* This is the implementation of the editor used when running on HorizonOS devices.
|
||||
*/
|
||||
open class GodotEditor : BaseGodotEditor() {
|
||||
|
||||
override fun getExcludedPermissions(): MutableSet<String> {
|
||||
val excludedPermissions = super.getExcludedPermissions().apply {
|
||||
// The AVATAR_CAMERA and HEADSET_CAMERA permissions are requested when `CameraFeed.feed_is_active`
|
||||
// is enabled.
|
||||
add("horizonos.permission.AVATAR_CAMERA")
|
||||
add("horizonos.permission.HEADSET_CAMERA")
|
||||
}
|
||||
return excludedPermissions
|
||||
}
|
||||
|
||||
override fun getXRRuntimePermissions(): MutableSet<String> {
|
||||
val xrRuntimePermissions = super.getXRRuntimePermissions()
|
||||
xrRuntimePermissions.add("com.oculus.permission.USE_SCENE")
|
||||
xrRuntimePermissions.add("horizonos.permission.USE_SCENE")
|
||||
return xrRuntimePermissions
|
||||
}
|
||||
}
|
134
platform/android/java/editor/src/main/AndroidManifest.xml
Normal file
134
platform/android/java/editor/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="auto">
|
||||
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="false"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00030000"
|
||||
android:required="true" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/themed_icon"
|
||||
android:label="${editorAppName}${editorBuildSuffix}"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/GodotEditorSplashScreenTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<profileable
|
||||
android:shell="true"
|
||||
android:enabled="true"
|
||||
tools:targetApi="29" />
|
||||
|
||||
<activity
|
||||
android:name=".GodotEditor"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="true"
|
||||
android:icon="@mipmap/themed_icon"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="userLandscape">
|
||||
<layout
|
||||
android:defaultWidth="@dimen/editor_default_window_width"
|
||||
android:defaultHeight="@dimen/editor_default_window_height" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter used to intercept hybrid PANEL launch for the current editor project, and route it
|
||||
properly through the editor 'run' logic (e.g: debugger setup) -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="org.godotengine.xr.hybrid.PANEL" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter used to intercept hybrid IMMERSIVE launch for the current editor project, and route it
|
||||
properly through the editor 'run' logic (e.g: debugger setup) -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="org.godotengine.xr.hybrid.IMMERSIVE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".GodotGame"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="false"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:launchMode="singleTask"
|
||||
android:process=":GodotGame"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:theme="@style/GodotGameTheme"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:screenOrientation="userLandscape">
|
||||
<layout
|
||||
android:defaultWidth="@dimen/editor_default_window_width"
|
||||
android:defaultHeight="@dimen/editor_default_window_height" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".embed.EmbeddedGodotGame"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="false"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:theme="@style/GodotEmbeddedGameTheme"
|
||||
android:taskAffinity=":embed"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:process=":EmbeddedGodotGame"
|
||||
android:supportsPictureInPicture="true" />
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":GodotXRGame"
|
||||
android:launchMode="singleTask"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:exported="false"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" />
|
||||
|
||||
<!--
|
||||
We remove this meta-data originating from the vendors plugin as we only need the loader for
|
||||
now since the project being edited provides its own version of the vendors plugin.
|
||||
|
||||
This needs to be removed once we start implementing the immersive version of the project
|
||||
manager and editor windows.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="org.godotengine.plugin.v2.GodotOpenXR"
|
||||
android:value="org.godotengine.openxr.vendors.GodotOpenXR"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user