feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

View 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/contributing/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.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under
[Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot)
per the Android logo usage guidelines:
> The Android robot is reproduced or modified from work created and shared by
> Google and used according to terms described in the Creative Commons 3.0
> Attribution License.

View file

@ -0,0 +1,114 @@
#!/usr/bin/env python
import subprocess
import sys
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",
]
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)
lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"])
# Needed to force rebuilding the platform files when the thirdparty code is updated.
env.Depends(lib, thirdparty_obj)
lib_arch_dir = ""
if env["arch"] == "arm32":
lib_arch_dir = "armeabi-v7a"
elif env["arch"] == "arm64":
lib_arch_dir = "arm64-v8a"
elif env["arch"] == "x86_32":
lib_arch_dir = "x86"
elif env["arch"] == "x86_64":
lib_arch_dir = "x86_64"
else:
print_warning("Architecture not suitable for embedding into APK; keeping .so at \\bin")
if lib_arch_dir != "":
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 = ""
out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
env_android.Command(
out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
)
stl_lib_path = (
str(env["ANDROID_NDK_ROOT"]) + "/sources/cxx-stl/llvm-libc++/libs/" + lib_arch_dir + "/libc++_shared.so"
)
env_android.Command(out_dir + "/libc++_shared.so", stl_lib_path, Copy("$TARGET", "$SOURCE"))
def generate_apk(target, source, env):
gradle_process = []
if sys.platform.startswith("win"):
gradle_process = [
"cmd",
"/c",
"gradlew.bat",
]
else:
gradle_process = ["./gradlew"]
gradle_process += [
"generateGodotEditor" if env["target"] == "editor" else "generateGodotTemplates",
"--quiet",
]
if env["debug_symbols"]:
gradle_process += ["-PdoNotStrip=true"]
subprocess.run(
gradle_process,
cwd="platform/android/java",
)
if env["generate_apk"]:
generate_apk_command = env_android.Command("generate_apk", [], generate_apk)
command = env_android.AlwaysBuild(generate_apk_command)
env_android.Depends(command, [lib])

View 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) {
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 = BitField<MouseButtonMask>(buttons_state.operator int64_t() ^ event_buttons_mask.operator int64_t());
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 (!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(BitField<MouseButtonMask>(event_buttons_mask.operator int64_t() ^ int64_t(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 (MouseButtonMask(button_mask.operator int64_t())) {
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;
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;
}

View file

@ -0,0 +1,109 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_INPUT_HANDLER_H
#define ANDROID_INPUT_HANDLER_H
#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;
};
private:
bool alt_mem = false;
bool shift_mem = false;
bool control_mem = false;
bool meta_mem = false;
BitField<MouseButtonMask> buttons_state;
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);
};
#endif // ANDROID_INPUT_HANDLER_H

View 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;
}

View file

@ -0,0 +1,200 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_KEYS_UTILS_H
#define ANDROID_KEYS_UTILS_H
#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);
#endif // ANDROID_KEYS_UTILS_H

View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* 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 and the
// `JNISingleton` registration occurs in
// `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup`
java_class_wrapper = memnew(JavaClassWrapper); // Dummy
GDREGISTER_CLASS(JNISingleton);
#endif
GDREGISTER_CLASS(JavaClass);
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 JavaClassWrapper::_bind_methods() {
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
}
#if !defined(ANDROID_ENABLED)
Variant JavaClass::callp(const StringName &, const Variant **, int, Callable::CallError &) {
return Variant();
}
JavaClass::JavaClass() {
}
Variant JavaObject::callp(const StringName &, const Variant **, int, Callable::CallError &) {
return Variant();
}
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
Ref<JavaClass> JavaClassWrapper::wrap(const String &) {
return Ref<JavaClass>();
}
JavaClassWrapper::JavaClassWrapper() {
singleton = this;
}
#endif

View file

@ -0,0 +1,37 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_API_H
#define ANDROID_API_H
void register_android_api();
void unregister_android_api();
#endif // ANDROID_API_H

View file

@ -0,0 +1,251 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef JAVA_CLASS_WRAPPER_H
#define JAVA_CLASS_WRAPPER_H
#include "core/object/ref_counted.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_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;
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:
r_type = Variant::STRING;
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:
r_type = Variant::PACKED_STRING_ARRAY;
break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS:
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;
HashMap<StringName, List<MethodInfo>> methods;
jclass _class;
#endif
public:
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
JavaClass();
};
class JavaObject : public RefCounted {
GDCLASS(JavaObject, RefCounted);
#ifdef ANDROID_ENABLED
Ref<JavaClass> base_class;
friend class JavaClass;
jobject instance;
#endif
public:
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
#ifdef ANDROID_ENABLED
JavaObject(const Ref<JavaClass> &p_base, jobject *p_instance);
~JavaObject();
#endif
};
class JavaClassWrapper : public Object {
GDCLASS(JavaClassWrapper, Object);
#ifdef ANDROID_ENABLED
RBMap<String, Ref<JavaClass>> class_cache;
friend class JavaClass;
jmethodID getDeclaredMethods;
jmethodID getFields;
jmethodID getParameterTypes;
jmethodID getReturnType;
jmethodID getModifiers;
jmethodID getName;
jmethodID Class_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
static JavaClassWrapper *singleton;
protected:
static void _bind_methods();
public:
static JavaClassWrapper *get_singleton() { return singleton; }
Ref<JavaClass> wrap(const String &p_class);
#ifdef ANDROID_ENABLED
JavaClassWrapper(jobject p_activity = nullptr);
#else
JavaClassWrapper();
#endif
};
#endif // JAVA_CLASS_WRAPPER_H

View file

@ -0,0 +1,251 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef JNI_SINGLETON_H
#define JNI_SINGLETON_H
#include "core/config/engine.h"
#include "core/variant/variant.h"
#ifdef ANDROID_ENABLED
#include "jni_utils.h"
#endif
class JNISingleton : public Object {
GDCLASS(JNISingleton, Object);
#ifdef ANDROID_ENABLED
struct MethodData {
jmethodID method;
Variant::Type ret_type;
Vector<Variant::Type> argtypes;
};
jobject instance;
RBMap<StringName, MethodData> method_map;
#endif
public:
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
#ifdef ANDROID_ENABLED
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) {
// The method is not in this map, defaulting to the regular instance calls.
return Object::callp(p_method, p_args, p_argcount, r_error);
}
ERR_FAIL_NULL_V(instance, Variant());
r_error.error = Callable::CallError::CALL_OK;
jvalue *v = nullptr;
if (p_argcount) {
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
}
JNIEnv *env = get_jni_env();
int res = env->PushLocalFrame(16);
ERR_FAIL_COND_V(res != 0, Variant());
List<jobject> to_erase;
for (int i = 0; i < p_argcount; i++) {
jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
v[i] = vr.val;
if (vr.obj) {
to_erase.push_back(vr.obj);
}
}
Variant ret;
switch (E->get().ret_type) {
case Variant::NIL: {
env->CallVoidMethodA(instance, E->get().method, v);
} break;
case Variant::BOOL: {
ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
} break;
case Variant::INT: {
ret = env->CallIntMethodA(instance, E->get().method, v);
} break;
case Variant::FLOAT: {
ret = env->CallFloatMethodA(instance, E->get().method, v);
} break;
case Variant::STRING: {
jobject o = env->CallObjectMethodA(instance, E->get().method, v);
ret = jstring_to_string((jstring)o, env);
env->DeleteLocalRef(o);
} break;
case Variant::PACKED_STRING_ARRAY: {
jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
ret = _jobject_to_variant(env, arr);
env->DeleteLocalRef(arr);
} break;
case Variant::PACKED_INT32_ARRAY: {
jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
int fCount = env->GetArrayLength(arr);
Vector<int> sarr;
sarr.resize(fCount);
int *w = sarr.ptrw();
env->GetIntArrayRegion(arr, 0, fCount, w);
ret = sarr;
env->DeleteLocalRef(arr);
} break;
case Variant::PACKED_INT64_ARRAY: {
jlongArray arr = (jlongArray)env->CallObjectMethodA(instance, E->get().method, v);
int fCount = env->GetArrayLength(arr);
Vector<int64_t> sarr;
sarr.resize(fCount);
int64_t *w = sarr.ptrw();
env->GetLongArrayRegion(arr, 0, fCount, w);
ret = sarr;
env->DeleteLocalRef(arr);
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
int fCount = env->GetArrayLength(arr);
Vector<float> sarr;
sarr.resize(fCount);
float *w = sarr.ptrw();
env->GetFloatArrayRegion(arr, 0, fCount, w);
ret = sarr;
env->DeleteLocalRef(arr);
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
jdoubleArray arr = (jdoubleArray)env->CallObjectMethodA(instance, E->get().method, v);
int fCount = env->GetArrayLength(arr);
Vector<double> sarr;
sarr.resize(fCount);
double *w = sarr.ptrw();
env->GetDoubleArrayRegion(arr, 0, fCount, w);
ret = sarr;
env->DeleteLocalRef(arr);
} break;
case Variant::DICTIONARY: {
jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
ret = _jobject_to_variant(env, obj);
env->DeleteLocalRef(obj);
} break;
default: {
env->PopLocalFrame(nullptr);
ERR_FAIL_V(Variant());
} break;
}
while (to_erase.size()) {
env->DeleteLocalRef(to_erase.front()->get());
to_erase.pop_front();
}
env->PopLocalFrame(nullptr);
return ret;
#else // ANDROID_ENABLED
// Defaulting to the regular instance calls.
return Object::callp(p_method, p_args, p_argcount, r_error);
#endif
}
#ifdef ANDROID_ENABLED
jobject get_instance() const {
return instance;
}
void set_instance(jobject p_instance) {
instance = p_instance;
}
void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
MethodData md;
md.method = p_method;
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);
}
#endif
JNISingleton() {
#ifdef ANDROID_ENABLED
instance = nullptr;
#endif
}
~JNISingleton() {
#ifdef ANDROID_ENABLED
method_map.clear();
if (instance) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
env->DeleteGlobalRef(instance);
}
#endif
}
};
#endif // JNI_SINGLETON_H

View file

@ -0,0 +1,363 @@
/**************************************************************************/
/* 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"
#include <string.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() {
}

View file

@ -0,0 +1,111 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef AUDIO_DRIVER_OPENSL_H
#define AUDIO_DRIVER_OPENSL_H
#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();
};
#endif // AUDIO_DRIVER_OPENSL_H

View file

@ -0,0 +1,211 @@
import os
import platform
import subprocess
import sys
from typing import TYPE_CHECKING
from methods import print_error, print_warning
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_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_apk", "Generate an APK/AAB after building Android library by calling Gradle", 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 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 "23.2.8568313"
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
def get_min_target_api():
return 21
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 = 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 configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
print_error(
'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
sys.exit(255)
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 = ndk_root + "/toolchains/llvm/prebuilt/" + host_subpath
compiler_path = toolchain_path + "/bin"
env["CC"] = compiler_path + "/clang"
env["CXX"] = compiler_path + "/clang++"
env["AR"] = compiler_path + "/llvm-ar"
env["RANLIB"] = compiler_path + "/llvm-ranlib"
env["AS"] = compiler_path + "/clang"
env.Append(
CCFLAGS=(
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
)
)
if get_min_sdk_version(env["ndk_platform"]) >= 24:
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
if env["arch"] == "x86_32":
# The NDK adds this if targeting API < 24, so we can drop it when Godot targets it at least
env.Append(CCFLAGS=["-mstackrealign"])
elif env["arch"] == "arm32":
env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
env.Append(CPPDEFINES=["__ARM_NEON__"])
elif env["arch"] == "arm64":
env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
env.Append(CCFLAGS=["-ffp-contract=off"])
# Link flags
env.Append(LINKFLAGS="-Wl,--gc-sections -Wl,--no-undefined -Wl,-z,now".split())
env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so")
env.Prepend(CPPPATH=["#platform/android"])
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
env.Append(LIBS=["OpenSLES", "EGL", "android", "log", "z", "dl"])
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
if env["opengl3"]:
env.Append(CPPDEFINES=["GLES3_ENABLED"])
env.Append(LIBS=["GLESv3"])

View file

@ -0,0 +1,360 @@
/**************************************************************************/
/* 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 "string_android.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, get_access_type(), 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, get_access_type(), 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, get_access_type(), 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, bd.length());
} 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_recursive(const 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(String p_dir) {
return make_dir_recursive(p_dir);
}
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", "(II)Ljava/lang/String;");
_dir_close = env->GetMethodID(cls, "dirClose", "(II)V");
_dir_is_dir = env->GetMethodID(cls, "dirIsDir", "(II)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", "(II)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, get_access_type(), p_id);
}
}

View file

@ -0,0 +1,108 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef DIR_ACCESS_JANDROID_H
#define DIR_ACCESS_JANDROID_H
#include "java_godot_lib_jni.h"
#include "core/io/dir_access.h"
#include "drivers/unix/dir_access_unix.h"
#include <stdio.h>
/// 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 {
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 FAILED; }
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);
};
#endif // DIR_ACCESS_JANDROID_H

View file

@ -0,0 +1,794 @@
/**************************************************************************/
/* 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_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:
//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::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();
}
}
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) {
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 {
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_screen_count() const {
return 1;
}
int DisplayServerAndroid::get_primary_screen() const {
return 0;
}
Point2i DisplayServerAndroid::screen_get_position(int p_screen) const {
return Point2i(0, 0);
}
Size2i DisplayServerAndroid::screen_get_size(int p_screen) const {
return OS_Android::get_singleton()->get_display_size();
}
Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const {
Size2i display_size = OS_Android::get_singleton()->get_display_size();
return Rect2i(0, 0, display_size.width, display_size.height);
}
int DisplayServerAndroid::screen_get_dpi(int p_screen) 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_screen_dpi();
}
float DisplayServerAndroid::screen_get_scale(int p_screen) const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
ERR_FAIL_NULL_V(godot_io_java, 1.0f);
float screen_scale = godot_io_java->get_scaled_density();
// Update the scale to avoid cropping.
Size2i screen_size = screen_get_size(p_screen);
if (screen_size != Size2i()) {
float width_scale = screen_size.width / (float)OS_Android::DEFAULT_WINDOW_WIDTH;
float height_scale = screen_size.height / (float)OS_Android::DEFAULT_WINDOW_HEIGHT;
screen_scale = MIN(screen_scale, MIN(width_scale, height_scale));
}
print_line("Selected screen scale: ", screen_scale);
return screen_scale;
}
float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
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();
}
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;
}
#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 {
return SCREEN_OF_MAIN_WINDOW;
}
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) {
// Not supported on Android.
}
DisplayServer::WindowMode DisplayServerAndroid::window_get_mode(DisplayServer::WindowID p_window) const {
return WINDOW_MODE_FULLSCREEN;
}
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 {
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, 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, 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, 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(GLES3_ENABLED)
if (rendering_driver == "opengl3") {
RasterizerGLES3::make_current(false);
}
#endif
#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) {
ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
memdelete(rendering_context);
rendering_context = nullptr;
r_error = ERR_UNAVAILABLE;
return;
}
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
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_set_mode(MouseMode p_mode) {
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 == p_mode) {
return;
}
if (p_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 (p_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 = p_mode;
}
DisplayServer::MouseMode DisplayServerAndroid::mouse_get_mode() const {
return mouse_mode;
}
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
}

View file

@ -0,0 +1,235 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef DISPLAY_SERVER_ANDROID_H
#define DISPLAY_SERVER_ANDROID_H
#include "servers/display_server.h"
#if defined(RD_ENABLED)
class RenderingContextDriver;
class RenderingDevice;
#endif
class DisplayServerAndroid : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
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;
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;
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;
void emit_system_theme_changed();
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;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
virtual bool clipboard_has() 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;
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 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;
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, 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;
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, Error &r_error);
~DisplayServerAndroid();
};
#endif // DISPLAY_SERVER_ANDROID_H

View file

@ -0,0 +1,604 @@
<?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="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] Although your binary may be smaller, your application may load slower because the native libraries are not loaded directly from the binary at runtime.
</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/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_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_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/immersive_mode" type="bool" setter="" getter="">
If [code]true[/code], hides navigation and status bar.
</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="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>

View file

@ -0,0 +1,67 @@
/**************************************************************************/
/* 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/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
void register_android_exporter_types() {
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformAndroid);
}
void register_android_exporter() {
#ifndef ANDROID_ENABLED
EDITOR_DEF("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("export/android/android_sdk_path", OS::get_singleton()->get_environment("ANDROID_HOME"));
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF("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("export/android/debug_keystore_user", DEFAULT_ANDROID_KEYSTORE_DEBUG_USER);
EDITOR_DEF("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));
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);
}

View file

@ -0,0 +1,37 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_EXPORT_H
#define ANDROID_EXPORT_H
void register_android_exporter_types();
void register_android_exporter();
#endif // ANDROID_EXPORT_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_EXPORT_PLUGIN_H
#define ANDROID_EXPORT_PLUGIN_H
#ifndef DISABLE_DEPRECATED
#include "godot_plugin_config.h"
#endif // DISABLE_DEPRECATED
#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 {
zipFile apk;
EditorProgress *ep = nullptr;
};
#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 String &p_name) const;
String get_package_name(const String &p_package) const;
String get_valid_basename() const;
String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
bool is_project_name_valid() 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);
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);
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_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions);
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
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);
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);
static void _create_editor_debug_keystore_if_needed();
static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
static bool _uses_vulkan();
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);
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, int 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_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, int 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, int 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, int 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();
};
#endif // ANDROID_EXPORT_PLUGIN_H

View 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

View file

@ -0,0 +1,108 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_GODOT_PLUGIN_CONFIG_H
#define ANDROID_GODOT_PLUGIN_CONFIG_H
#ifndef DISABLE_DEPRECATED
#include "core/config/project_settings.h"
#include "core/error/error_list.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
#endif // ANDROID_GODOT_PLUGIN_CONFIG_H

View file

@ -0,0 +1,340 @@
/**************************************************************************/
/* 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_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) {
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
print_verbose("Saving project files from " + p_path + " into " + dst_path);
Error err = store_file_at_path(dst_path, p_data);
return err;
}
String _android_xml_escape(const String &p_string) {
// Android XML requires strings to be both valid XML (`xml_escape()`) but also
// to escape characters which are valid XML but have special meaning in Android XML.
// https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
// Note: Didn't handle U+XXXX unicode chars, could be done if needed.
return p_string
.replace("@", "\\@")
.replace("?", "\\?")
.replace("'", "\\'")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\t", "\\t")
.xml_escape(false);
}
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(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();
Dictionary appnames = GLOBAL_GET("application/config/name_localized");
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 (appnames.has(locale)) {
String locale_project_name = appnames[locale];
String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name));
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
store_string_at_path(locale_directory, processed_xml_string);
} else {
// 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" };
size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]);
for (size_t i = 0; i < num_sizes; i++) {
String feature_name = vformat("screen/support_%s", sizes[i]);
String feature_support = bool_to_string(p_preset->get(feature_name));
String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support);
manifest_screen_sizes += xml_entry;
}
manifest_screen_sizes += " />\n";
return manifest_screen_sizes;
}
String _get_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(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
"tools:node=\"mergeOnlyAttributes\" "
"android:excludeFromRecents=\"%s\" "
"android:screenOrientation=\"%s\" "
"android:resizeableActivity=\"%s\">\n",
bool_to_string(p_preset->get("package/exclude_from_recents")),
orientation,
bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
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) {
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:appCategory=\"%s\"\n"
" android:isGame=\"%s\"\n"
" android:hasFragileUserData=\"%s\"\n"
" android:requestLegacyExternalStorage=\"%s\"\n"
" tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
" tools:ignore=\"GoogleAppIndexingWarning\">\n\n",
bool_to_string(p_preset->get("user_data_backup/allow")),
_get_app_category_label(app_category_index),
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));
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;
}

View file

@ -0,0 +1,110 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef ANDROID_GRADLE_EXPORT_UTIL_H
#define ANDROID_GRADLE_EXPORT_UTIL_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"
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;
// 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 {
String assets_directory;
String libs_directory;
bool debug;
Vector<String> libs;
};
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);
// 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);
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir);
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);
#endif // ANDROID_GRADLE_EXPORT_UTIL_H

View 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: 421 B

View 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: 431 B

View file

@ -0,0 +1,276 @@
/**************************************************************************/
/* 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, path.length());
} else if (path.begins_with("res://")) {
path = path.substr(6, path.length());
}
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, ERR_UNAVAILABLE); //can't write on android..
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;
}
uint8_t FileAccessAndroid::get_8() const {
if (pos >= len) {
eof = true;
return 0;
}
uint8_t byte;
AAsset_read(asset, &byte, 1);
pos++;
return byte;
}
uint16_t FileAccessAndroid::get_16() const {
if (pos >= len) {
eof = true;
return 0;
}
uint16_t bytes = 0;
int r = AAsset_read(asset, &bytes, 2);
if (r >= 0) {
pos += r;
if (pos >= len) {
eof = true;
}
}
if (big_endian) {
bytes = BSWAP16(bytes);
}
return bytes;
}
uint32_t FileAccessAndroid::get_32() const {
if (pos >= len) {
eof = true;
return 0;
}
uint32_t bytes = 0;
int r = AAsset_read(asset, &bytes, 4);
if (r >= 0) {
pos += r;
if (pos >= len) {
eof = true;
}
}
if (big_endian) {
bytes = BSWAP32(bytes);
}
return bytes;
}
uint64_t FileAccessAndroid::get_64() const {
if (pos >= len) {
eof = true;
return 0;
}
uint64_t bytes = 0;
int r = AAsset_read(asset, &bytes, 8);
if (r >= 0) {
pos += r;
if (pos >= len) {
eof = true;
}
}
if (big_endian) {
bytes = BSWAP64(bytes);
}
return bytes;
}
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;
}
Error FileAccessAndroid::get_error() const {
return eof ? ERR_FILE_EOF : OK; // not sure what else it may happen
}
void FileAccessAndroid::flush() {
ERR_FAIL();
}
void FileAccessAndroid::store_8(uint8_t p_dest) {
ERR_FAIL();
}
void FileAccessAndroid::store_16(uint16_t p_dest) {
ERR_FAIL();
}
void FileAccessAndroid::store_32(uint32_t p_dest) {
ERR_FAIL();
}
void FileAccessAndroid::store_64(uint64_t p_dest) {
ERR_FAIL();
}
bool FileAccessAndroid::file_exists(const String &p_path) {
String path = fix_path(p_path).simplify_path();
if (path.begins_with("/")) {
path = path.substr(1, path.length());
} else if (path.begins_with("res://")) {
path = path.substr(6, path.length());
}
AAsset *at = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
if (!at) {
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);
}

View file

@ -0,0 +1,105 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef FILE_ACCESS_ANDROID_H
#define FILE_ACCESS_ANDROID_H
#include "core/io/file_access.h"
#include <android/asset_manager.h>
#include <android/log.h>
#include <jni.h>
#include <stdio.h>
class FileAccessAndroid : public 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 uint8_t get_8() const override; // get a byte
virtual uint16_t get_16() const override;
virtual uint32_t get_32() const override;
virtual uint64_t get_64() const override;
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 void store_8(uint8_t p_dest) override; // store a byte
virtual void store_16(uint16_t p_dest) override;
virtual void store_32(uint32_t p_dest) override;
virtual void store_64(uint64_t p_dest) 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 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 FAILED; }
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();
};
#endif // FILE_ACCESS_ANDROID_H

View file

@ -0,0 +1,436 @@
/**************************************************************************/
/* 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_resize = 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) {
switch (res) {
case 0:
default:
return ERR_FILE_CANT_OPEN;
case -2:
return ERR_FILE_NOT_FOUND;
}
}
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);
}
}
uint8_t FileAccessFilesystemJAndroid::get_8() const {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
uint8_t byte;
get_buffer(&byte, 1);
return byte;
}
uint16_t FileAccessFilesystemJAndroid::get_16() const {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
uint16_t bytes = 0;
get_buffer(reinterpret_cast<uint8_t *>(&bytes), 2);
if (big_endian) {
bytes = BSWAP16(bytes);
}
return bytes;
}
uint32_t FileAccessFilesystemJAndroid::get_32() const {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
uint32_t bytes = 0;
get_buffer(reinterpret_cast<uint8_t *>(&bytes), 4);
if (big_endian) {
bytes = BSWAP32(bytes);
}
return bytes;
}
uint64_t FileAccessFilesystemJAndroid::get_64() const {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
uint64_t bytes = 0;
get_buffer(reinterpret_cast<uint8_t *>(&bytes), 8);
if (big_endian) {
bytes = BSWAP64(bytes);
}
return bytes;
}
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.parse_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
return String();
}
return result;
}
}
}
if (result.parse_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;
}
}
void FileAccessFilesystemJAndroid::store_8(uint8_t p_dest) {
store_buffer(&p_dest, 1);
}
void FileAccessFilesystemJAndroid::store_16(uint16_t p_dest) {
if (big_endian) {
p_dest = BSWAP16(p_dest);
}
store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 2);
}
void FileAccessFilesystemJAndroid::store_32(uint32_t p_dest) {
if (big_endian) {
p_dest = BSWAP32(p_dest);
}
store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 4);
}
void FileAccessFilesystemJAndroid::store_64(uint64_t p_dest) {
if (big_endian) {
p_dest = BSWAP64(p_dest);
}
store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 8);
}
void FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
if (_file_write) {
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
if (p_length == 0) {
return;
}
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
jobject j_buffer = env->NewDirectByteBuffer((void *)p_src, p_length);
env->CallVoidMethod(file_access_handler, _file_write, id, j_buffer);
env->DeleteLocalRef(j_buffer);
}
}
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);
switch (res) {
case 0:
return OK;
case -4:
return ERR_INVALID_PARAMETER;
case -3:
return ERR_FILE_CANT_OPEN;
case -2:
return ERR_FILE_NOT_FOUND;
case -1:
default:
return FAILED;
}
} 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, false);
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;
}
}
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;)V");
_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_resize = env->GetMethodID(cls, "fileResize", "(IJ)I");
}
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();
}
}

View file

@ -0,0 +1,117 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef FILE_ACCESS_FILESYSTEM_JANDROID_H
#define FILE_ACCESS_FILESYSTEM_JANDROID_H
#include "java_godot_lib_jni.h"
#include "core/io/file_access.h"
class FileAccessFilesystemJAndroid : public 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_resize;
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 uint8_t get_8() const override; ///< get a byte
virtual uint16_t get_16() const override;
virtual uint32_t get_32() const override;
virtual uint64_t get_64() const 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 void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_16(uint16_t p_dest) override;
virtual void store_32(uint32_t p_dest) override;
virtual void store_64(uint64_t p_dest) override;
virtual void 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 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 FAILED; }
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();
};
#endif // FILE_ACCESS_FILESYSTEM_JANDROID_H

View file

@ -0,0 +1,58 @@
<?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" />
<!-- Records the version of the Godot editor used for building -->
<meta-data
android:name="org.godotengine.editor.version"
android:value="${godotEditorVersion}" />
<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:configChanges="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>

View file

@ -0,0 +1,10 @@
plugins {
id 'com.android.asset-pack'
}
assetPack {
packName = "installTime" // Directory name for the asset pack
dynamicDelivery {
deliveryType = "install-time" // Delivery mode
}
}

View file

@ -0,0 +1,2 @@
*
!.gitignore

View file

@ -0,0 +1,301 @@
// 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/" }
// 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 {}
}
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)
}
}
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
ndkVersion versions.ndkVersion
compileOptions {
sourceCompatibility versions.javaVersion
targetCompatibility versions.javaVersion
}
kotlinOptions {
jvmTarget = versions.javaVersion
}
assetPacks = [":assetPacks:installTime"]
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 {
String[] export_abi_list = getExportEnabledABIs()
abiFilters export_abi_list
}
manifestPlaceholders = [godotEditorVersion: getGodotEditorVersion()]
// 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'
// 'doNotStrip' is enabled for development within Android Studio
if (shouldNotStrip()) {
doNotStrip '**/*.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()
}
}
}
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
}
}
}
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 ->
output.outputFileName = "android_${variant.name}.apk"
}
}
}
task copyAndRenameDebugApk(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")
from "$buildDir/outputs/apk/debug/android_debug.apk"
into getExportPath()
rename "android_debug.apk", getExportFilename()
}
task copyAndRenameDevApk(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")
from "$buildDir/outputs/apk/dev/android_dev.apk"
into getExportPath()
rename "android_dev.apk", getExportFilename()
}
task copyAndRenameReleaseApk(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")
from "$buildDir/outputs/apk/release/android_release.apk"
into getExportPath()
rename "android_release.apk", getExportFilename()
}
task copyAndRenameDebugAab(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")
from "$buildDir/outputs/bundle/debug/build-debug.aab"
into getExportPath()
rename "build-debug.aab", getExportFilename()
}
task copyAndRenameDevAab(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")
from "$buildDir/outputs/bundle/dev/build-dev.aab"
into getExportPath()
rename "build-dev.aab", getExportFilename()
}
task copyAndRenameReleaseAab(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")
from "$buildDir/outputs/bundle/release/build-release.aab"
into getExportPath()
rename "build-release.aab", getExportFilename()
}
/**
* Used to validate the version of the Java SDK used for the Godot gradle builds.
*/
task validateJavaVersion {
if (JavaVersion.current() != versions.javaVersion) {
throw new GradleException("Invalid Java version ${JavaVersion.current()}. Version ${versions.javaVersion} is the required 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"))
}

View file

@ -0,0 +1,386 @@
ext.versions = [
androidGradlePlugin: '8.2.0',
compileSdk : 34,
// Also update 'platform/android/export/export_plugin.cpp#OPENGL_MIN_SDK_VERSION'
minSdk : 21,
// Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
targetSdk : 34,
buildTools : '34.0.0',
kotlinVersion : '1.9.20',
fragmentVersion : '1.7.1',
nexusPublishVersion: '1.3.0',
javaVersion : JavaVersion.VERSION_17,
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
ndkVersion : '23.2.8568313',
splashscreenVersion: '1.0.1'
]
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.getGodotEditorVersion = { ->
String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
if (editorVersion == null || editorVersion.isEmpty()) {
// Try the library version first
editorVersion = getGodotLibraryVersionName()
if (editorVersion.isEmpty()) {
// Fallback value.
editorVersion = "custom_build"
}
}
return editorVersion
}
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
}
/**
* 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 = { ->
int minSdk = getExportMinSdkVersion()
if (minSdk < 23) {
// Enforce the default behavior for compatibility with device running api < 23
return true
}
String legacyPackagingFlag = project.hasProperty("compress_native_libraries") ? project.property("compress_native_libraries") : ""
if (legacyPackagingFlag != null && !legacyPackagingFlag.isEmpty()) {
return Boolean.parseBoolean(legacyPackagingFlag)
}
// Default behavior for minSdk >= 23
return false
}

View 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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar"/>
<style name="GodotAppSplashTheme" parent="Theme.SplashScreen">
<!-- Set the splash screen background, animated icon, and animation
duration. -->
<item name="android:windowSplashScreenBackground">@mipmap/icon_background</item>
<!-- Use windowSplashScreenAnimatedIcon to add a drawable or an animated
drawable. One of these is required. -->
<item name="windowSplashScreenAnimatedIcon">@mipmap/icon_foreground</item>
<!-- Set the theme of the Activity that directly follows your splash
screen. This is required. -->
<item name="postSplashScreenTheme">@style/GodotAppMainTheme</item>
</style>
</resources>

View file

@ -0,0 +1,17 @@
// 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/" }
}
}
include ':assetPacks:installTime'

View file

@ -0,0 +1,49 @@
/**************************************************************************/
/* 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.GodotActivity;
import android.os.Bundle;
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 {
@Override
public void onCreate(Bundle savedInstanceState) {
SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
}
}

View file

@ -0,0 +1,366 @@
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/" }
}
}
ext {
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
supportedFlavors = ["editor", "template"]
supportedFlavorsBuildTypes = [
"editor": ["dev", "debug", "release"],
"template": ["dev", "debug", "release"]
]
// 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"]
}
def rootDir = "../../.."
def binDir = "$rootDir/bin/"
def androidEditorBuildsDir = "$binDir/android_editor_builds/"
def getSconsTaskName(String flavor, String buildType, String abi) {
return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}
/**
* Copy the generated 'android_debug.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
task copyDebugBinaryToBin(type: Copy) {
dependsOn ':app:assembleDebug'
from('app/build/outputs/apk/debug')
into(binDir)
include('android_debug.apk')
}
/**
* Copy the generated 'android_dev.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
task copyDevBinaryToBin(type: Copy) {
dependsOn ':app:assembleDev'
from('app/build/outputs/apk/dev')
into(binDir)
include('android_dev.apk')
}
/**
* Copy the generated 'android_release.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
task copyReleaseBinaryToBin(type: Copy) {
dependsOn ':app:assembleRelease'
from('app/build/outputs/apk/release')
into(binDir)
include('android_release.apk')
}
/**
* Copy the Godot android library archive debug file into the app module debug libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
include('godot-lib.template_debug.aar')
}
/**
* Copy the Godot android library archive debug file into the root bin directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.template_debug.aar')
}
/**
* Copy the Godot android library archive dev file into the app module dev libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDevAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateDev'
from('lib/build/outputs/aar')
into('app/libs/dev')
include('godot-lib.template_debug.dev.aar')
}
/**
* Copy the Godot android library archive dev file into the root bin directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDevAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateDev'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.template_debug.dev.aar')
}
/**
* Copy the Godot android library archive release file into the app module release libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into('app/libs/release')
include('godot-lib.template_release.aar')
}
/**
* Copy the Godot android library archive release file into the root bin directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.template_release.aar')
}
/**
* 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 || 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.lifecycle("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')
*/
def generateBuildTasks(String flavor = "template") {
if (!supportedFlavors.contains(flavor)) {
throw new GradleException("Invalid build flavor: $flavor")
}
def tasks = []
// Only build the apks and aar 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)
if (!excludeSconsBuildTasks || (targetLibs != null
&& targetLibs.isDirectory()
&& targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0)) {
String capitalizedTarget = target.capitalize()
if (isTemplate) {
// Copy the generated aar library files to the build directory.
tasks += "copy${capitalizedTarget}AARToAppModule"
// Copy the generated aar library files to the bin directory.
tasks += "copy${capitalizedTarget}AARToBin"
// Copy the prebuilt binary templates to the bin directory.
tasks += "copy${capitalizedTarget}BinaryToBin"
} else {
// Copy the generated editor apk to the bin directory.
tasks += "copyEditor${capitalizedTarget}ApkToBin"
// Copy the generated editor aab to the bin directory.
tasks += "copyEditor${capitalizedTarget}AabToBin"
}
} else {
logger.lifecycle("No native shared libs for target $target. Skipping build.")
}
}
return tasks
}
task copyEditorReleaseApkToBin(type: Copy) {
dependsOn ':editor:assembleRelease'
from('editor/build/outputs/apk/release')
into(androidEditorBuildsDir)
include('android_editor-release*.apk')
}
task copyEditorReleaseAabToBin(type: Copy) {
dependsOn ':editor:bundleRelease'
from('editor/build/outputs/bundle/release')
into(androidEditorBuildsDir)
include('android_editor-release*.aab')
}
task copyEditorDebugApkToBin(type: Copy) {
dependsOn ':editor:assembleDebug'
from('editor/build/outputs/apk/debug')
into(androidEditorBuildsDir)
include('android_editor-debug.apk')
}
task copyEditorDebugAabToBin(type: Copy) {
dependsOn ':editor:bundleDebug'
from('editor/build/outputs/bundle/debug')
into(androidEditorBuildsDir)
include('android_editor-debug.aab')
}
task copyEditorDevApkToBin(type: Copy) {
dependsOn ':editor:assembleDev'
from('editor/build/outputs/apk/dev')
into(androidEditorBuildsDir)
include('android_editor-dev.apk')
}
task copyEditorDevAabToBin(type: Copy) {
dependsOn ':editor:bundleDev'
from('editor/build/outputs/bundle/dev')
into(androidEditorBuildsDir)
include('android_editor-dev.aab')
}
/**
* Generate the Godot Editor Android apk.
*
* 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 apk(s) for which the shared libraries is available.
*/
task generateGodotEditor {
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = generateBuildTasks("editor")
}
/**
* 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'
}
/**
* Generates the same output as generateGodotTemplates but with dev symbols
*/
task generateDevTemplate {
// add parameter to set symbols to true
project.ext.doNotStrip = "true"
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = generateBuildTasks("template")
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_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")
}

View file

@ -0,0 +1,156 @@
// Gradle build config for Godot Engine's Android port.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'base'
}
dependencies {
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.1.4"
}
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) {
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 Editor 4",
editorBuildSuffix: ""
]
}
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 {
// 'doNotStrip' is enabled for development within Android Studio
if (shouldNotStrip()) {
doNotStrip '**/*.so'
}
}
}

View file

@ -0,0 +1 @@
!/debug

View file

@ -0,0 +1,72 @@
<?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-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" />
<application
android:allowBackup="false"
android:icon="@mipmap/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="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:exported="true"
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>
</activity>
<activity
android:name=".GodotGame"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:exported="false"
android:label="@string/godot_project_name_string"
android:launchMode="singleTask"
android:process=":GodotGame"
android:screenOrientation="userLandscape">
<layout
android:defaultWidth="@dimen/editor_default_window_width"
android:defaultHeight="@dimen/editor_default_window_height" />
</activity>
</application>
</manifest>

View file

@ -0,0 +1,68 @@
/**************************************************************************/
/* EditorWindowInfo.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
/**
* Specifies the policy for adjacent launches.
*/
enum class LaunchAdjacentPolicy {
/**
* Adjacent launches are disabled.
*/
DISABLED,
/**
* Adjacent launches are enabled / disabled based on the device and screen metrics.
*/
AUTO,
/**
* Adjacent launches are enabled.
*/
ENABLED
}
/**
* Describe the editor window to launch
*/
data class EditorWindowInfo(
val windowClassName: String,
val windowId: Int,
val processNameSuffix: String,
val launchAdjacentPolicy: LaunchAdjacentPolicy = LaunchAdjacentPolicy.DISABLED
) {
constructor(
windowClass: Class<*>,
windowId: Int,
processNameSuffix: String,
launchAdjacentPolicy: LaunchAdjacentPolicy = LaunchAdjacentPolicy.DISABLED
) : this(windowClass.name, windowId, processNameSuffix, launchAdjacentPolicy)
}

View file

@ -0,0 +1,353 @@
/**************************************************************************/
/* 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
import android.Manifest
import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.*
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.annotation.CallSuper
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.window.layout.WindowMetricsCalculator
import org.godotengine.godot.GodotActivity
import org.godotengine.godot.GodotLib
import org.godotengine.godot.utils.PermissionsUtil
import org.godotengine.godot.utils.ProcessPhoenix
import java.util.*
import kotlin.math.min
/**
* Base class for the Godot Android Editor activities.
*
* This provides the basic templates for the activities making up this application.
* Each derived activity runs in its own process, which enable up to have several instances of
* the Godot engine up and running at the same time.
*
* It also plays the role of the primary editor window.
*/
open class GodotEditor : GodotActivity() {
companion object {
private val TAG = GodotEditor::class.java.simpleName
private const val WAIT_FOR_DEBUGGER = false
private const val EXTRA_COMMAND_LINE_PARAMS = "command_line_params"
// Command line arguments
private const val EDITOR_ARG = "--editor"
private const val EDITOR_ARG_SHORT = "-e"
private const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
private const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
// Info for the various classes used by the editor
internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "")
internal val RUN_GAME_INFO = EditorWindowInfo(GodotGame::class.java, 667, ":GodotGame", LaunchAdjacentPolicy.AUTO)
/**
* Sets of constants to specify the window to use to run the project.
*
* Should match the values in 'editor/editor_settings.cpp' for the
* 'run/window_placement/android_window' setting.
*/
private const val ANDROID_WINDOW_AUTO = 0
private const val ANDROID_WINDOW_SAME_AS_EDITOR = 1
private const val ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR = 2
}
private val commandLineParams = ArrayList<String>()
private val editorLoadingIndicator: View? by lazy { findViewById(R.id.editor_loading_indicator) }
override fun getGodotAppLayout() = R.layout.godot_editor_layout
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
// We exclude certain permissions from the set we request at startup, as they'll be
// requested on demand based on use-cases.
PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}")
updateCommandLineParams(params?.asList() ?: emptyList())
if (BuildConfig.BUILD_TYPE == "dev" && WAIT_FOR_DEBUGGER) {
Debug.waitForDebugger()
}
super.onCreate(savedInstanceState)
}
override fun onGodotSetupCompleted() {
super.onGodotSetupCompleted()
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
checkForProjectPermissionsToEnable()
runOnUiThread {
// Enable long press, panning and scaling gestures
godotFragment?.godot?.renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
}
}
}
override fun onGodotMainLoopStarted() {
super.onGodotMainLoopStarted()
runOnUiThread {
// Hide the loading indicator
editorLoadingIndicator?.visibility = View.GONE
}
}
/**
* Check for project permissions to enable
*/
protected open fun checkForProjectPermissionsToEnable() {
// Check for RECORD_AUDIO permission
val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"))
if (audioInputEnabled) {
PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
}
}
@CallSuper
protected open fun updateCommandLineParams(args: List<String>) {
// Update the list of command line params with the new args
commandLineParams.clear()
if (args.isNotEmpty()) {
commandLineParams.addAll(args)
}
if (BuildConfig.BUILD_TYPE == "dev") {
commandLineParams.add("--benchmark")
}
}
final override fun getCommandLine() = commandLineParams
protected open fun getEditorWindowInfo(args: Array<String>): EditorWindowInfo {
var hasEditor = false
var i = 0
while (i < args.size) {
when (args[i++]) {
EDITOR_ARG, EDITOR_ARG_SHORT, EDITOR_PROJECT_MANAGER_ARG, EDITOR_PROJECT_MANAGER_ARG_SHORT -> hasEditor = true
}
}
return if (hasEditor) {
EDITOR_MAIN_INFO
} else {
RUN_GAME_INFO
}
}
protected open fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? {
return when (instanceId) {
RUN_GAME_INFO.windowId -> RUN_GAME_INFO
EDITOR_MAIN_INFO.windowId -> EDITOR_MAIN_INFO
else -> null
}
}
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
val editorWindowInfo = getEditorWindowInfo(args)
// Launch a new activity
val newInstance = Intent()
.setComponent(ComponentName(this, editorWindowInfo.windowClassName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_COMMAND_LINE_PARAMS, args)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (editorWindowInfo.launchAdjacentPolicy == LaunchAdjacentPolicy.ENABLED ||
(editorWindowInfo.launchAdjacentPolicy == LaunchAdjacentPolicy.AUTO && shouldGameLaunchAdjacent())) {
Log.v(TAG, "Adding flag for adjacent launch")
newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)
}
}
if (editorWindowInfo.windowClassName == javaClass.name) {
Log.d(TAG, "Restarting ${editorWindowInfo.windowClassName} with parameters ${args.contentToString()}")
val godot = godot
if (godot != null) {
godot.destroyAndKillProcess {
ProcessPhoenix.triggerRebirth(this, newInstance)
}
} else {
ProcessPhoenix.triggerRebirth(this, newInstance)
}
} else {
Log.d(TAG, "Starting ${editorWindowInfo.windowClassName} with parameters ${args.contentToString()}")
newInstance.putExtra(EXTRA_NEW_LAUNCH, true)
startActivity(newInstance)
}
return editorWindowInfo.windowId
}
final override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
val editorWindowInfo = getEditorWindowInfoForInstanceId(godotInstanceId) ?: return super.onGodotForceQuit(godotInstanceId)
if (editorWindowInfo.windowClassName == javaClass.name) {
Log.d(TAG, "Force quitting ${editorWindowInfo.windowClassName}")
ProcessPhoenix.forceQuit(this)
return true
}
val processName = packageName + editorWindowInfo.processNameSuffix
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val runningProcesses = activityManager.runningAppProcesses
for (runningProcess in runningProcesses) {
if (runningProcess.processName == processName) {
// Killing process directly
Log.v(TAG, "Killing Godot process ${runningProcess.processName}")
Process.killProcess(runningProcess.pid)
return true
}
}
return super.onGodotForceQuit(godotInstanceId)
}
// Get the screen's density scale
private val isLargeScreen: Boolean
// Get the minimum window size // Correspond to the EXPANDED window size class.
get() {
val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
// Get the screen's density scale
val scale = resources.displayMetrics.density
// Get the minimum window size
val minSize = min(metrics.bounds.width(), metrics.bounds.height()).toFloat()
val minSizeDp = minSize / scale
return minSizeDp >= 840f // Correspond to the EXPANDED window size class.
}
override fun setRequestedOrientation(requestedOrientation: Int) {
if (!overrideOrientationRequest()) {
super.setRequestedOrientation(requestedOrientation)
}
}
/**
* The Godot Android Editor sets its own orientation via its AndroidManifest
*/
protected open fun overrideOrientationRequest() = true
/**
* Enable long press gestures for the Godot Android editor.
*/
protected open fun enableLongPressGestures() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_long_press_as_right_click"))
/**
* Enable pan and scale gestures for the Godot Android editor.
*/
protected open fun enablePanAndScaleGestures() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
/**
* Whether we should launch the new godot instance in an adjacent window
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
*/
private fun shouldGameLaunchAdjacent(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
when (Integer.parseInt(GodotLib.getEditorSetting("run/window_placement/android_window"))) {
ANDROID_WINDOW_SAME_AS_EDITOR -> false
ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> true
else -> {
// ANDROID_WINDOW_AUTO
isInMultiWindowMode || isLargeScreen
}
}
} catch (e: NumberFormatException) {
// Fall-back to the 'Auto' behavior
isInMultiWindowMode || isLargeScreen
}
} else {
false
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
if (requestCode == PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Toast.makeText(
this,
R.string.denied_storage_permission_error_msg,
Toast.LENGTH_LONG
).show()
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// Check if we got access to the necessary storage permissions
if (requestCode == PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
var hasReadAccess = false
var hasWriteAccess = false
for (i in permissions.indices) {
if (Manifest.permission.READ_EXTERNAL_STORAGE == permissions[i] && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
hasReadAccess = true
}
if (Manifest.permission.WRITE_EXTERNAL_STORAGE == permissions[i] && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
hasWriteAccess = true
}
}
if (!hasReadAccess || !hasWriteAccess) {
Toast.makeText(
this,
R.string.denied_storage_permission_error_msg,
Toast.LENGTH_LONG
).show()
}
}
}
}
}

View file

@ -0,0 +1,52 @@
/**************************************************************************/
/* GodotGame.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
import org.godotengine.godot.GodotLib
/**
* Drives the 'run project' window of the Godot Editor.
*/
class GodotGame : GodotEditor() {
override fun getGodotAppLayout() = org.godotengine.godot.R.layout.godot_app_layout
override fun overrideOrientationRequest() = false
override fun enableLongPressGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
override fun checkForProjectPermissionsToEnable() {
// Nothing to do.. by the time we get here, the project permissions will have already
// been requested by the Editor window.
}
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/godot_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
style="@android:style/Widget.Holo.ProgressBar.Large"
android:id="@+id/editor_loading_indicator"
android:layout_width="80dp"
android:layout_height="80dp"
android:indeterminate="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.80"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="editor_default_window_height">600dp</dimen>
<dimen name="editor_default_window_width">1024dp</dimen>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="denied_storage_permission_error_msg">Missing storage access permission!</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="GodotEditorTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
</style>
<style name="GodotEditorSplashScreenTheme" parent="Theme.SplashScreen.IconBackground">
<!-- Set the theme of the Activity that directly follows your splash
screen. This is required. -->
<item name="postSplashScreenTheme">@style/GodotEditorTheme</item>
</style>
</resources>

View file

@ -0,0 +1,31 @@
# Project-wide Gradle settings.
# NOTE: This should be kept in sync with 'godot/platform/android/java/app/gradle.properties' except
# where otherwise specified.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# 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
# Disable resource optimizations for template release build.
# NOTE: This is turned on for Godot Editor's gradle builds in order to improve the release build.
android.enableResourceOptimizations=false
# Fix gradle build errors when the build path contains non-ASCII characters
android.overridePathCheck=true

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Wed Jan 17 12:08:26 PST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
engine/platform/android/java/gradlew vendored Executable file
View file

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

View file

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0">
<application>
<!-- Records the version of the Godot library -->
<meta-data
android:name="org.godotengine.library.version"
android:value="${godotLibraryVersion}" />
<service android:name=".GodotDownloaderService" />
<activity
android:name=".utils.ProcessPhoenix"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:process=":phoenix"
android:exported="false"
/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/godot_provider_paths" />
</provider>
</application>
</manifest>

Some files were not shown because too many files have changed in this diff Show more