feat: modules moved and engine moved to submodule
This commit is contained in:
parent
dfb5e645cd
commit
c33d2130cc
5136 changed files with 225275 additions and 64485 deletions
|
|
@ -12,7 +12,7 @@ using [Gradle](https://gradle.org/) as a build system.
|
|||
|
||||
## Artwork license
|
||||
|
||||
[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under
|
||||
[`logo.svg`](export/logo.svg) and [`run_icon.svg`](export/run_icon.svg) are licensed under
|
||||
[Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot)
|
||||
per the Android logo usage guidelines:
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ 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"])
|
||||
# FIXME: Hardcoded to bin to ensure the directory exists if not redirecting build objects.
|
||||
lib = env_android.add_shared_library("#bin/libgodot", android_objects, redirect_build_objects=False)
|
||||
|
||||
# Needed to force rebuilding the platform files when the thirdparty code is updated.
|
||||
env.Depends(lib, thirdparty_obj)
|
||||
|
|
@ -78,14 +79,12 @@ if lib_arch_dir != "":
|
|||
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")
|
||||
)
|
||||
env_android.CommandNoCache(out_dir + "/libgodot_android.so", lib, 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"))
|
||||
env_android.CommandNoCache(out_dir + "/libc++_shared.so", stl_lib_path, Copy("$TARGET", "$SOURCE"))
|
||||
|
||||
def generate_apk(target, source, env):
|
||||
gradle_process = []
|
||||
|
|
@ -113,6 +112,4 @@ if lib_arch_dir != "":
|
|||
)
|
||||
|
||||
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])
|
||||
env_android.AlwaysBuild(env_android.CommandNoCache("generate_apk", lib, env.Run(generate_apk)))
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> even
|
|||
}
|
||||
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());
|
||||
BitField<MouseButtonMask> changed_button_mask = buttons_state.get_different(event_buttons_mask);
|
||||
|
||||
buttons_state = event_buttons_mask;
|
||||
|
||||
|
|
@ -337,7 +337,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
|
|||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_MOVE: {
|
||||
if (!mouse_event_info.valid) {
|
||||
if (!p_source_mouse_relative && !mouse_event_info.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -395,7 +395,7 @@ void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_bu
|
|||
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_button_mask(event_buttons_mask.get_different(mouse_button_to_mask(wheel_button)));
|
||||
evd->set_factor(factor);
|
||||
Input::get_singleton()->parse_input_event(evd);
|
||||
Ref<InputEventMouseButton> evdd = evd->duplicate();
|
||||
|
|
@ -423,7 +423,7 @@ void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
|
|||
}
|
||||
|
||||
MouseButton AndroidInputHandler::_button_index_from_mask(BitField<MouseButtonMask> button_mask) {
|
||||
switch (MouseButtonMask(button_mask.operator int64_t())) {
|
||||
switch (button_mask) {
|
||||
case MouseButtonMask::LEFT:
|
||||
return MouseButton::LEFT;
|
||||
case MouseButtonMask::RIGHT:
|
||||
|
|
@ -440,7 +440,7 @@ MouseButton AndroidInputHandler::_button_index_from_mask(BitField<MouseButtonMas
|
|||
}
|
||||
|
||||
BitField<MouseButtonMask> AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
|
||||
BitField<MouseButtonMask> godot_button_mask;
|
||||
BitField<MouseButtonMask> godot_button_mask = MouseButtonMask::NONE;
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
|
||||
godot_button_mask.set_flag(MouseButtonMask::LEFT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_INPUT_HANDLER_H
|
||||
#define ANDROID_INPUT_HANDLER_H
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input.h"
|
||||
|
||||
|
|
@ -63,7 +62,7 @@ public:
|
|||
int index = 0; // Can be either JoyAxis or JoyButton.
|
||||
bool pressed = false;
|
||||
float value = 0;
|
||||
BitField<HatMask> hat;
|
||||
BitField<HatMask> hat = HatMask::CENTER;
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -72,7 +71,7 @@ private:
|
|||
bool control_mem = false;
|
||||
bool meta_mem = false;
|
||||
|
||||
BitField<MouseButtonMask> buttons_state;
|
||||
BitField<MouseButtonMask> buttons_state = MouseButtonMask::NONE;
|
||||
|
||||
Vector<TouchPos> touch;
|
||||
MouseEventInfo mouse_event_info;
|
||||
|
|
@ -105,5 +104,3 @@ public:
|
|||
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
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_KEYS_UTILS_H
|
||||
#define ANDROID_KEYS_UTILS_H
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
|
|
@ -196,5 +195,3 @@ static AndroidGodotLocationPair android_godot_location_pairs[] = {
|
|||
};
|
||||
|
||||
KeyLocation godot_location_from_android_code(unsigned int p_code);
|
||||
|
||||
#endif // ANDROID_KEYS_UTILS_H
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_API_H
|
||||
#define ANDROID_API_H
|
||||
#pragma once
|
||||
|
||||
void register_android_api();
|
||||
void unregister_android_api();
|
||||
|
||||
#endif // ANDROID_API_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JAVA_CLASS_WRAPPER_H
|
||||
#define JAVA_CLASS_WRAPPER_H
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
|
@ -296,5 +295,3 @@ public:
|
|||
#endif
|
||||
JavaClassWrapper();
|
||||
};
|
||||
|
||||
#endif // JAVA_CLASS_WRAPPER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JNI_SINGLETON_H
|
||||
#define JNI_SINGLETON_H
|
||||
#pragma once
|
||||
|
||||
#include "java_class_wrapper.h"
|
||||
|
||||
|
|
@ -103,5 +102,3 @@ public:
|
|||
wrapped_object.unref();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JNI_SINGLETON_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef AUDIO_DRIVER_OPENSL_H
|
||||
#define AUDIO_DRIVER_OPENSL_H
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
|
@ -107,5 +106,3 @@ public:
|
|||
|
||||
AudioDriverOpenSL();
|
||||
};
|
||||
|
||||
#endif // AUDIO_DRIVER_OPENSL_H
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ def configure(env: "SConsEnvironment"):
|
|||
has_swappy = detect_swappy()
|
||||
if not has_swappy:
|
||||
print_warning(
|
||||
"Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/darksylinc/godot-swappy/releases and extract it so that the following files can be found:\n"
|
||||
"Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/godotengine/godot-swappy/releases and extract it so that the following files can be found:\n"
|
||||
+ " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/x86/libswappy_static.a\n"
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DIALOG_UTILS_JNI_H
|
||||
#define DIALOG_UTILS_JNI_H
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
|
@ -37,5 +36,3 @@ extern "C" {
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text);
|
||||
}
|
||||
|
||||
#endif // DIALOG_UTILS_JNI_H
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ String DirAccessJAndroid::get_current_dir(bool p_include_drive) const {
|
|||
if (bd.begins_with(root_string)) {
|
||||
return bd;
|
||||
} else if (bd.begins_with("/")) {
|
||||
return root_string + bd.substr(1, bd.length());
|
||||
return root_string + bd.substr(1);
|
||||
} else {
|
||||
return root_string + bd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DIR_ACCESS_JANDROID_H
|
||||
#define DIR_ACCESS_JANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "java_godot_lib_jni.h"
|
||||
|
||||
|
|
@ -43,6 +42,7 @@
|
|||
/// The implementation use jni in order to comply with Android filesystem
|
||||
/// access restriction.
|
||||
class DirAccessJAndroid : public DirAccessUnix {
|
||||
GDSOFTCLASS(DirAccessJAndroid, DirAccessUnix);
|
||||
static jobject dir_access_handler;
|
||||
static jclass cls;
|
||||
|
||||
|
|
@ -104,5 +104,3 @@ private:
|
|||
void dir_close(int p_id);
|
||||
String get_absolute_path(String p_path);
|
||||
};
|
||||
|
||||
#endif // DIR_ACCESS_JANDROID_H
|
||||
|
|
|
|||
|
|
@ -145,6 +145,16 @@ void DisplayServerAndroid::emit_system_theme_changed() {
|
|||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::set_hardware_keyboard_connection_change_callback(const Callable &p_callable) {
|
||||
hardware_keyboard_connection_changed = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::emit_hardware_keyboard_connection_changed(bool p_connected) {
|
||||
if (hardware_keyboard_connection_changed.is_valid()) {
|
||||
hardware_keyboard_connection_changed.call_deferred(p_connected);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAndroid::clipboard_set(const String &p_text) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL(godot_java);
|
||||
|
|
@ -204,7 +214,7 @@ void DisplayServerAndroid::emit_input_dialog_callback(String p_text) {
|
|||
}
|
||||
}
|
||||
|
||||
Error DisplayServerAndroid::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
|
||||
Error DisplayServerAndroid::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) {
|
||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||
file_picker_callback = p_callback;
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISPLAY_SERVER_ANDROID_H
|
||||
#define DISPLAY_SERVER_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
|
|
@ -39,7 +38,7 @@ class RenderingDevice;
|
|||
#endif
|
||||
|
||||
class DisplayServerAndroid : public DisplayServer {
|
||||
// No need to register with GDCLASS, it's platform-specific and nothing is added.
|
||||
GDSOFTCLASS(DisplayServerAndroid, DisplayServer);
|
||||
|
||||
String rendering_driver;
|
||||
|
||||
|
|
@ -90,6 +89,7 @@ class DisplayServerAndroid : public DisplayServer {
|
|||
Callable rect_changed_callback;
|
||||
|
||||
Callable system_theme_changed;
|
||||
Callable hardware_keyboard_connection_changed;
|
||||
|
||||
Callable dialog_callback;
|
||||
Callable input_dialog_callback;
|
||||
|
|
@ -115,11 +115,10 @@ public:
|
|||
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;
|
||||
void emit_system_theme_changed();
|
||||
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
|
|
@ -131,7 +130,7 @@ public:
|
|||
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||
void emit_input_dialog_callback(String p_text);
|
||||
|
||||
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, const FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
|
||||
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, const FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) override;
|
||||
void emit_file_picker_callback(bool p_ok, const Vector<String> &p_selected_paths);
|
||||
|
||||
virtual Color get_accent_color() const override;
|
||||
|
|
@ -160,6 +159,8 @@ public:
|
|||
virtual void virtual_keyboard_hide() override;
|
||||
virtual int virtual_keyboard_get_height() const override;
|
||||
virtual bool has_hardware_keyboard() const override;
|
||||
virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) override;
|
||||
void emit_hardware_keyboard_connection_changed(bool p_connected);
|
||||
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
|
@ -257,5 +258,3 @@ public:
|
|||
DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerAndroid();
|
||||
};
|
||||
|
||||
#endif // DISPLAY_SERVER_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@
|
|||
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. See [method DisplayServer.window_set_mode] to toggle it at runtime.
|
||||
If [code]true[/code], hides the navigation and status bar. Set [method DisplayServer.window_set_mode] to change this at runtime.
|
||||
</member>
|
||||
<member name="screen/support_large" type="bool" setter="" getter="">
|
||||
Indicates whether the application supports larger screen form-factors.
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_EXPORT_H
|
||||
#define ANDROID_EXPORT_H
|
||||
#pragma once
|
||||
|
||||
void register_android_exporter_types();
|
||||
void register_android_exporter();
|
||||
|
||||
#endif // ANDROID_EXPORT_H
|
||||
|
|
|
|||
|
|
@ -30,11 +30,9 @@
|
|||
|
||||
#include "export_plugin.h"
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
#include "logo_svg.gen.h"
|
||||
#include "run_icon_svg.gen.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
|
|
@ -396,28 +394,27 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
|||
// its format is "[property]: [value]" so changed it as like build.prop
|
||||
String p = props[j];
|
||||
p = p.replace("]: ", "=");
|
||||
p = p.replace("[", "");
|
||||
p = p.replace("]", "");
|
||||
p = p.remove_chars("[]");
|
||||
|
||||
if (p.begins_with("ro.product.model=")) {
|
||||
device = p.get_slice("=", 1).strip_edges();
|
||||
device = p.get_slicec('=', 1).strip_edges();
|
||||
} else if (p.begins_with("ro.product.brand=")) {
|
||||
vendor = p.get_slice("=", 1).strip_edges().capitalize();
|
||||
vendor = p.get_slicec('=', 1).strip_edges().capitalize();
|
||||
} else if (p.begins_with("ro.build.display.id=")) {
|
||||
d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n";
|
||||
d.description += "Build: " + p.get_slicec('=', 1).strip_edges() + "\n";
|
||||
} else if (p.begins_with("ro.build.version.release=")) {
|
||||
d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n";
|
||||
d.description += "Release: " + p.get_slicec('=', 1).strip_edges() + "\n";
|
||||
} else if (p.begins_with("ro.build.version.sdk=")) {
|
||||
d.api_level = p.get_slice("=", 1).to_int();
|
||||
d.api_level = p.get_slicec('=', 1).to_int();
|
||||
} else if (p.begins_with("ro.product.cpu.abi=")) {
|
||||
d.architecture = p.get_slice("=", 1).strip_edges();
|
||||
d.architecture = p.get_slicec('=', 1).strip_edges();
|
||||
d.description += "CPU: " + d.architecture + "\n";
|
||||
} else if (p.begins_with("ro.product.manufacturer=")) {
|
||||
d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n";
|
||||
d.description += "Manufacturer: " + p.get_slicec('=', 1).strip_edges() + "\n";
|
||||
} else if (p.begins_with("ro.board.platform=")) {
|
||||
d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n";
|
||||
d.description += "Chipset: " + p.get_slicec('=', 1).strip_edges() + "\n";
|
||||
} else if (p.begins_with("ro.opengles.version=")) {
|
||||
uint32_t opengl = p.get_slice("=", 1).to_int();
|
||||
uint32_t opengl = p.get_slicec('=', 1).to_int();
|
||||
d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl) & 0xFF) + "\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -480,32 +477,32 @@ void EditorExportPlatformAndroid::_update_preset_status() {
|
|||
}
|
||||
#endif
|
||||
|
||||
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
|
||||
String EditorExportPlatformAndroid::get_project_name(const Ref<EditorExportPreset> &p_preset, const String &p_name) const {
|
||||
String aname;
|
||||
if (!p_name.is_empty()) {
|
||||
aname = p_name;
|
||||
} else {
|
||||
aname = GLOBAL_GET("application/config/name");
|
||||
aname = get_project_setting(p_preset, "application/config/name");
|
||||
}
|
||||
|
||||
if (aname.is_empty()) {
|
||||
aname = VERSION_NAME;
|
||||
aname = GODOT_VERSION_NAME;
|
||||
}
|
||||
|
||||
return aname;
|
||||
}
|
||||
|
||||
String EditorExportPlatformAndroid::get_package_name(const String &p_package) const {
|
||||
String EditorExportPlatformAndroid::get_package_name(const Ref<EditorExportPreset> &p_preset, const String &p_package) const {
|
||||
String pname = p_package;
|
||||
String name = get_valid_basename();
|
||||
String name = get_valid_basename(p_preset);
|
||||
pname = pname.replace("$genname", name);
|
||||
return pname;
|
||||
}
|
||||
|
||||
// Returns the project name without invalid characters
|
||||
// or the "noname" string if all characters are invalid.
|
||||
String EditorExportPlatformAndroid::get_valid_basename() const {
|
||||
String basename = GLOBAL_GET("application/config/name");
|
||||
String EditorExportPlatformAndroid::get_valid_basename(const Ref<EditorExportPreset> &p_preset) const {
|
||||
String basename = get_project_setting(p_preset, "application/config/name");
|
||||
basename = basename.to_lower();
|
||||
|
||||
String name;
|
||||
|
|
@ -533,8 +530,8 @@ String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportP
|
|||
return gradle_build_directory.path_join(p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY);
|
||||
}
|
||||
|
||||
bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const {
|
||||
String pname = get_package_name(p_package);
|
||||
bool EditorExportPlatformAndroid::is_package_name_valid(const Ref<EditorExportPreset> &p_preset, const String &p_package, String *r_error) const {
|
||||
String pname = get_package_name(p_preset, p_package);
|
||||
|
||||
if (pname.length() == 0) {
|
||||
if (r_error) {
|
||||
|
|
@ -596,12 +593,12 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformAndroid::is_project_name_valid() const {
|
||||
bool EditorExportPlatformAndroid::is_project_name_valid(const Ref<EditorExportPreset> &p_preset) const {
|
||||
// Get the original project name and convert to lowercase.
|
||||
String basename = GLOBAL_GET("application/config/name");
|
||||
String basename = get_project_setting(p_preset, "application/config/name");
|
||||
basename = basename.to_lower();
|
||||
// Check if there are invalid characters.
|
||||
if (basename != get_valid_basename()) {
|
||||
if (basename != get_valid_basename(p_preset)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -804,7 +801,12 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
|
|||
|
||||
Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) {
|
||||
APKExportData *ed = static_cast<APKExportData *>(p_userdata);
|
||||
const String path = ResourceUID::ensure_path(p_path);
|
||||
|
||||
String path = p_path.simplify_path();
|
||||
if (path.begins_with("uid://")) {
|
||||
path = ResourceUID::uid_to_path(path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, path));
|
||||
}
|
||||
const String dst_path = path.replace_first("res://", "assets/");
|
||||
|
||||
store_in_apk(ed, dst_path, p_data, _should_compress_asset(path, p_data) ? Z_DEFLATED : 0);
|
||||
|
|
@ -855,9 +857,9 @@ bool EditorExportPlatformAndroid::_has_manage_external_storage_permission(const
|
|||
return p_permissions.has("android.permission.MANAGE_EXTERNAL_STORAGE");
|
||||
}
|
||||
|
||||
bool EditorExportPlatformAndroid::_uses_vulkan() {
|
||||
String rendering_method = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
|
||||
String rendering_driver = GLOBAL_GET("rendering/rendering_device/driver.android");
|
||||
bool EditorExportPlatformAndroid::_uses_vulkan(const Ref<EditorExportPreset> &p_preset) const {
|
||||
String rendering_method = get_project_setting(p_preset, "rendering/renderer/rendering_method.mobile");
|
||||
String rendering_driver = get_project_setting(p_preset, "rendering/rendering_device/driver.android");
|
||||
return (rendering_method == "forward_plus" || rendering_method == "mobile") && rendering_driver == "vulkan";
|
||||
}
|
||||
|
||||
|
|
@ -939,7 +941,7 @@ void EditorExportPlatformAndroid::_create_editor_debug_keystore_if_needed() {
|
|||
print_verbose("Updated editor debug keystore to " + keystore_path);
|
||||
}
|
||||
|
||||
void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
|
||||
void EditorExportPlatformAndroid::_get_manifest_info(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions, Vector<FeatureInfo> &r_features, Vector<MetadataInfo> &r_metadata) {
|
||||
const char **aperms = ANDROID_PERMS;
|
||||
while (*aperms) {
|
||||
bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
|
||||
|
|
@ -960,6 +962,36 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset>
|
|||
r_permissions.push_back("android.permission.INTERNET");
|
||||
}
|
||||
}
|
||||
|
||||
if (_uses_vulkan(p_preset)) {
|
||||
// Require vulkan hardware level 1 support
|
||||
FeatureInfo vulkan_level = {
|
||||
"android.hardware.vulkan.level", // name
|
||||
false, // required
|
||||
"1" // version
|
||||
};
|
||||
r_features.append(vulkan_level);
|
||||
|
||||
// Require vulkan version 1.0
|
||||
FeatureInfo vulkan_version = {
|
||||
"android.hardware.vulkan.version", // name
|
||||
true, // required
|
||||
"0x400003" // version - Encoded value for api version 1.0
|
||||
};
|
||||
r_features.append(vulkan_version);
|
||||
}
|
||||
|
||||
MetadataInfo rendering_method_metadata = {
|
||||
"org.godotengine.rendering.method",
|
||||
p_preset->get_project_setting("rendering/renderer/rendering_method.mobile")
|
||||
};
|
||||
r_metadata.append(rendering_method_metadata);
|
||||
|
||||
MetadataInfo editor_version_metadata = {
|
||||
"org.godotengine.editor.version",
|
||||
String(GODOT_VERSION_FULL_CONFIG)
|
||||
};
|
||||
r_metadata.append(editor_version_metadata);
|
||||
}
|
||||
|
||||
void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
|
||||
|
|
@ -973,7 +1005,9 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||
manifest_text += _get_gles_tag();
|
||||
|
||||
Vector<String> perms;
|
||||
_get_permissions(p_preset, p_give_internet, perms);
|
||||
Vector<FeatureInfo> features;
|
||||
Vector<MetadataInfo> manifest_metadata;
|
||||
_get_manifest_info(p_preset, p_give_internet, perms, features, manifest_metadata);
|
||||
for (int i = 0; i < perms.size(); i++) {
|
||||
String permission = perms.get(i);
|
||||
if (permission == "android.permission.WRITE_EXTERNAL_STORAGE" || (permission == "android.permission.READ_EXTERNAL_STORAGE" && _has_manage_external_storage_permission(perms))) {
|
||||
|
|
@ -983,9 +1017,8 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||
}
|
||||
}
|
||||
|
||||
if (_uses_vulkan()) {
|
||||
manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"false\" android:version=\"1\" />\n";
|
||||
manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.version\" android:required=\"true\" android:version=\"0x400003\" />\n";
|
||||
for (int i = 0; i < features.size(); i++) {
|
||||
manifest_text += vformat(" <uses-feature tools:node=\"replace\" android:name=\"%s\" android:required=\"%s\" android:version=\"%s\" />\n", features[i].name, features[i].required, features[i].version);
|
||||
}
|
||||
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
|
|
@ -999,7 +1032,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||
}
|
||||
}
|
||||
|
||||
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug);
|
||||
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug, manifest_metadata);
|
||||
manifest_text += "</manifest>\n";
|
||||
String manifest_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join(vformat("src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")));
|
||||
|
||||
|
|
@ -1087,7 +1120,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
String package_name = p_preset->get("package/unique_name");
|
||||
|
||||
const int screen_orientation =
|
||||
_get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
|
||||
_get_android_orientation_value(DisplayServer::ScreenOrientation(int(get_project_setting(p_preset, "display/window/handheld/orientation"))));
|
||||
|
||||
bool screen_support_small = p_preset->get("screen/support_small");
|
||||
bool screen_support_normal = p_preset->get("screen/support_normal");
|
||||
|
|
@ -1098,11 +1131,12 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
int app_category = p_preset->get("package/app_category");
|
||||
bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall");
|
||||
bool exclude_from_recents = p_preset->get("package/exclude_from_recents");
|
||||
bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable"));
|
||||
bool is_resizeable = bool(get_project_setting(p_preset, "display/window/size/resizable"));
|
||||
|
||||
Vector<String> perms;
|
||||
// Write permissions into the perms variable.
|
||||
_get_permissions(p_preset, p_give_internet, perms);
|
||||
Vector<FeatureInfo> features;
|
||||
Vector<MetadataInfo> manifest_metadata;
|
||||
_get_manifest_info(p_preset, p_give_internet, perms, features, manifest_metadata);
|
||||
bool has_read_write_storage_permission = _has_read_write_storage_permission(perms);
|
||||
|
||||
while (ofs < (uint32_t)p_manifest.size()) {
|
||||
|
|
@ -1171,7 +1205,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
|
||||
//replace project information
|
||||
if (tname == "manifest" && attrname == "package") {
|
||||
string_table.write[attr_value] = get_package_name(package_name);
|
||||
string_table.write[attr_value] = get_package_name(p_preset, package_name);
|
||||
}
|
||||
|
||||
if (tname == "manifest" && attrname == "versionCode") {
|
||||
|
|
@ -1219,7 +1253,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
}
|
||||
|
||||
if (tname == "provider" && attrname == "authorities") {
|
||||
string_table.write[attr_value] = get_package_name(package_name) + String(".fileprovider");
|
||||
string_table.write[attr_value] = get_package_name(p_preset, package_name) + String(".fileprovider");
|
||||
}
|
||||
|
||||
if (tname == "supports-screens") {
|
||||
|
|
@ -1246,42 +1280,25 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
|
||||
String tname = string_table[name];
|
||||
|
||||
if (tname == "uses-feature") {
|
||||
Vector<String> feature_names;
|
||||
Vector<bool> feature_required_list;
|
||||
Vector<int> feature_versions;
|
||||
if (tname == "manifest" || tname == "application") {
|
||||
// save manifest ending so we can restore it
|
||||
Vector<uint8_t> manifest_end;
|
||||
uint32_t manifest_cur_size = p_manifest.size();
|
||||
|
||||
if (_uses_vulkan()) {
|
||||
// Require vulkan hardware level 1 support
|
||||
feature_names.push_back("android.hardware.vulkan.level");
|
||||
feature_required_list.push_back(false);
|
||||
feature_versions.push_back(1);
|
||||
manifest_end.resize(p_manifest.size() - ofs);
|
||||
memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
|
||||
|
||||
// Require vulkan version 1.0
|
||||
feature_names.push_back("android.hardware.vulkan.version");
|
||||
feature_required_list.push_back(true);
|
||||
feature_versions.push_back(0x400003); // Encoded value for api version 1.0
|
||||
int32_t attr_name_string = string_table.find("name");
|
||||
ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
|
||||
|
||||
int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android");
|
||||
if (ns_android_string == -1) {
|
||||
string_table.push_back("http://schemas.android.com/apk/res/android");
|
||||
ns_android_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
if (feature_names.size() > 0) {
|
||||
ofs += 24; // skip over end tag
|
||||
|
||||
// save manifest ending so we can restore it
|
||||
Vector<uint8_t> manifest_end;
|
||||
uint32_t manifest_cur_size = p_manifest.size();
|
||||
|
||||
manifest_end.resize(p_manifest.size() - ofs);
|
||||
memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
|
||||
|
||||
int32_t attr_name_string = string_table.find("name");
|
||||
ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
|
||||
|
||||
int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android");
|
||||
if (ns_android_string == -1) {
|
||||
string_table.push_back("http://schemas.android.com/apk/res/android");
|
||||
ns_android_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
if (tname == "manifest") {
|
||||
// Updating manifest features
|
||||
int32_t attr_uses_feature_string = string_table.find("uses-feature");
|
||||
if (attr_uses_feature_string == -1) {
|
||||
string_table.push_back("uses-feature");
|
||||
|
|
@ -1294,11 +1311,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
attr_required_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < feature_names.size(); i++) {
|
||||
const String &feature_name = feature_names[i];
|
||||
bool feature_required = feature_required_list[i];
|
||||
int feature_version = feature_versions[i];
|
||||
bool has_version_attribute = feature_version != -1;
|
||||
for (int i = 0; i < features.size(); i++) {
|
||||
const String &feature_name = features[i].name;
|
||||
bool feature_required = features[i].required;
|
||||
String feature_version = features[i].version;
|
||||
bool has_version_attribute = !feature_version.is_empty();
|
||||
|
||||
print_line("Adding feature " + feature_name);
|
||||
|
||||
|
|
@ -1326,9 +1343,9 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
attr_version_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
version_value = string_table.find(itos(feature_version));
|
||||
version_value = string_table.find(feature_version);
|
||||
if (version_value == -1) {
|
||||
string_table.push_back(itos(feature_version));
|
||||
string_table.push_back(feature_version);
|
||||
version_value = string_table.size() - 1;
|
||||
}
|
||||
|
||||
|
|
@ -1400,79 +1417,149 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
|
||||
ofs += 24;
|
||||
}
|
||||
memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
|
||||
ofs -= 24; // go back over back end
|
||||
}
|
||||
}
|
||||
if (tname == "manifest") {
|
||||
// save manifest ending so we can restore it
|
||||
Vector<uint8_t> manifest_end;
|
||||
uint32_t manifest_cur_size = p_manifest.size();
|
||||
|
||||
manifest_end.resize(p_manifest.size() - ofs);
|
||||
memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
|
||||
|
||||
int32_t attr_name_string = string_table.find("name");
|
||||
ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
|
||||
|
||||
int32_t ns_android_string = string_table.find("android");
|
||||
ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace.");
|
||||
|
||||
int32_t attr_uses_permission_string = string_table.find("uses-permission");
|
||||
if (attr_uses_permission_string == -1) {
|
||||
string_table.push_back("uses-permission");
|
||||
attr_uses_permission_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < perms.size(); ++i) {
|
||||
print_line("Adding permission " + perms[i]);
|
||||
|
||||
manifest_cur_size += 56 + 24; // node + end node
|
||||
p_manifest.resize(manifest_cur_size);
|
||||
|
||||
// Add permission to the string pool
|
||||
int32_t perm_string = string_table.find(perms[i]);
|
||||
if (perm_string == -1) {
|
||||
string_table.push_back(perms[i]);
|
||||
perm_string = string_table.size() - 1;
|
||||
// Updating manifest permissions
|
||||
int32_t attr_uses_permission_string = string_table.find("uses-permission");
|
||||
if (attr_uses_permission_string == -1) {
|
||||
string_table.push_back("uses-permission");
|
||||
attr_uses_permission_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
// start tag
|
||||
encode_uint16(0x102, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(56, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
|
||||
encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
|
||||
encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
|
||||
encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs
|
||||
encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
|
||||
for (int i = 0; i < perms.size(); ++i) {
|
||||
print_line("Adding permission " + perms[i]);
|
||||
|
||||
// attribute
|
||||
encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
|
||||
encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
|
||||
encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value
|
||||
encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
|
||||
p_manifest.write[ofs + 50] = 0; // typedvalue_always0
|
||||
p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
|
||||
encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference
|
||||
manifest_cur_size += 56 + 24; // node + end node
|
||||
p_manifest.resize(manifest_cur_size);
|
||||
|
||||
ofs += 56;
|
||||
// Add permission to the string pool
|
||||
int32_t perm_string = string_table.find(perms[i]);
|
||||
if (perm_string == -1) {
|
||||
string_table.push_back(perms[i]);
|
||||
perm_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
// end tag
|
||||
encode_uint16(0x103, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(24, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
|
||||
// start tag
|
||||
encode_uint16(0x102, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(56, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
|
||||
encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
|
||||
encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
|
||||
encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs
|
||||
encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
|
||||
|
||||
ofs += 24;
|
||||
// attribute
|
||||
encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
|
||||
encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
|
||||
encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value
|
||||
encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
|
||||
p_manifest.write[ofs + 50] = 0; // typedvalue_always0
|
||||
p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
|
||||
encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference
|
||||
|
||||
ofs += 56;
|
||||
|
||||
// end tag
|
||||
encode_uint16(0x103, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(24, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
|
||||
|
||||
ofs += 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (tname == "application") {
|
||||
// Updating application meta-data
|
||||
int32_t attr_meta_data_string = string_table.find("meta-data");
|
||||
if (attr_meta_data_string == -1) {
|
||||
string_table.push_back("meta-data");
|
||||
attr_meta_data_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
int32_t attr_value_string = string_table.find("value");
|
||||
if (attr_value_string == -1) {
|
||||
string_table.push_back("value");
|
||||
attr_value_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < manifest_metadata.size(); i++) {
|
||||
String meta_data_name = manifest_metadata[i].name;
|
||||
String meta_data_value = manifest_metadata[i].value;
|
||||
|
||||
print_line("Adding application metadata " + meta_data_name);
|
||||
|
||||
int32_t meta_data_name_string = string_table.find(meta_data_name);
|
||||
if (meta_data_name_string == -1) {
|
||||
string_table.push_back(meta_data_name);
|
||||
meta_data_name_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
int32_t meta_data_value_string = string_table.find(meta_data_value);
|
||||
if (meta_data_value_string == -1) {
|
||||
string_table.push_back(meta_data_value);
|
||||
meta_data_value_string = string_table.size() - 1;
|
||||
}
|
||||
|
||||
int tag_size = 76; // node and two attrs + end node
|
||||
int attr_count = 2;
|
||||
manifest_cur_size += tag_size + 24;
|
||||
p_manifest.resize(manifest_cur_size);
|
||||
|
||||
// start tag
|
||||
encode_uint16(0x102, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_meta_data_string, &p_manifest.write[ofs + 20]); // name
|
||||
encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
|
||||
encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
|
||||
encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs
|
||||
encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
|
||||
encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
|
||||
|
||||
// android:name attribute
|
||||
encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
|
||||
encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
|
||||
encode_uint32(meta_data_name_string, &p_manifest.write[ofs + 44]); // raw_value
|
||||
encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
|
||||
p_manifest.write[ofs + 50] = 0; // typedvalue_always0
|
||||
p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
|
||||
encode_uint32(meta_data_name_string, &p_manifest.write[ofs + 52]); // typedvalue reference
|
||||
|
||||
// android:value attribute
|
||||
encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns
|
||||
encode_uint32(attr_value_string, &p_manifest.write[ofs + 60]); // 'value'
|
||||
encode_uint32(meta_data_value_string, &p_manifest.write[ofs + 64]); // raw_value
|
||||
encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size
|
||||
p_manifest.write[ofs + 70] = 0; // typedvalue_always0
|
||||
p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string)
|
||||
encode_uint32(meta_data_value_string, &p_manifest.write[ofs + 72]); // typedvalue reference
|
||||
|
||||
ofs += 76;
|
||||
|
||||
// end tag
|
||||
encode_uint16(0x103, &p_manifest.write[ofs]); // type
|
||||
encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
|
||||
encode_uint32(24, &p_manifest.write[ofs + 4]); // size
|
||||
encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
|
||||
encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
|
||||
encode_uint32(attr_meta_data_string, &p_manifest.write[ofs + 20]); // name
|
||||
|
||||
ofs += 24;
|
||||
}
|
||||
}
|
||||
|
||||
// copy footer back in
|
||||
|
|
@ -1484,7 +1571,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
ofs += size;
|
||||
}
|
||||
|
||||
//create new andriodmanifest binary
|
||||
// Create new android manifest binary.
|
||||
|
||||
Vector<uint8_t> ret;
|
||||
ret.resize(string_table_begins + string_table.size() * 4);
|
||||
|
|
@ -1588,9 +1675,7 @@ String EditorExportPlatformAndroid::_parse_string(const uint8_t *p_bytes, bool p
|
|||
str8.write[i] = p_bytes[offset + i];
|
||||
}
|
||||
str8.write[len] = 0;
|
||||
String str;
|
||||
str.parse_utf8((const char *)str8.ptr(), len);
|
||||
return str;
|
||||
return String::utf8((const char *)str8.ptr(), len);
|
||||
} else {
|
||||
String str;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
|
|
@ -1615,7 +1700,7 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &
|
|||
Vector<String> string_table;
|
||||
|
||||
String package_name = p_preset->get("package/name");
|
||||
Dictionary appnames = GLOBAL_GET("application/config/name_localized");
|
||||
Dictionary appnames = get_project_setting(p_preset, "application/config/name_localized");
|
||||
|
||||
for (uint32_t i = 0; i < string_count; i++) {
|
||||
uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]);
|
||||
|
|
@ -1626,14 +1711,14 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &
|
|||
if (str.begins_with("godot-project-name")) {
|
||||
if (str == "godot-project-name") {
|
||||
//project name
|
||||
str = get_project_name(package_name);
|
||||
str = get_project_name(p_preset, package_name);
|
||||
|
||||
} else {
|
||||
String lang = str.substr(str.rfind_char('-') + 1, str.length()).replace("-", "_");
|
||||
String lang = str.substr(str.rfind_char('-') + 1).replace_char('-', '_');
|
||||
if (appnames.has(lang)) {
|
||||
str = appnames[lang];
|
||||
} else {
|
||||
str = get_project_name(package_name);
|
||||
str = get_project_name(p_preset, package_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1725,7 +1810,7 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
|
|||
}
|
||||
|
||||
void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome) {
|
||||
String project_icon_path = GLOBAL_GET("application/config/icon");
|
||||
String project_icon_path = get_project_setting(p_preset, "application/config/icon");
|
||||
|
||||
Error err = OK;
|
||||
|
||||
|
|
@ -1854,7 +1939,7 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
|
|||
String pn = p_preset->get("package/unique_name");
|
||||
String pn_err;
|
||||
|
||||
if (!is_package_name_valid(pn, &pn_err)) {
|
||||
if (!is_package_name_valid(Ref<EditorExportPreset>(p_preset), pn, &pn_err)) {
|
||||
return TTR("Invalid package name:") + " " + pn_err;
|
||||
}
|
||||
} else if (p_name == "gesture/swipe_to_dismiss") {
|
||||
|
|
@ -1864,16 +1949,10 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
|
|||
}
|
||||
} else if (p_name == "gradle_build/use_gradle_build") {
|
||||
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||
String enabled_plugins_names = _get_plugins_names(Ref<EditorExportPreset>(p_preset));
|
||||
if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) {
|
||||
String enabled_deprecated_plugins_names = _get_deprecated_plugins_names(Ref<EditorExportPreset>(p_preset));
|
||||
if (!enabled_deprecated_plugins_names.is_empty() && !gradle_build_enabled) {
|
||||
return TTR("\"Use Gradle Build\" must be enabled to use the plugins.");
|
||||
}
|
||||
} else if (p_name == "xr_features/xr_mode") {
|
||||
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||
int xr_mode_index = p_preset->get("xr_features/xr_mode");
|
||||
if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) {
|
||||
return TTR("OpenXR requires \"Use Gradle Build\" to be enabled");
|
||||
}
|
||||
} else if (p_name == "gradle_build/compress_native_libraries") {
|
||||
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||
if (bool(p_preset->get("gradle_build/compress_native_libraries")) && !gradle_build_enabled) {
|
||||
|
|
@ -2041,7 +2120,9 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor
|
|||
if (p_option == "graphics/opengl_debug" ||
|
||||
p_option == "command_line/extra_args" ||
|
||||
p_option == "permissions/custom_permissions" ||
|
||||
p_option == "gradle_build/compress_native_libraries" ||
|
||||
p_option == "keystore/debug" ||
|
||||
p_option == "keystore/debug_user" ||
|
||||
p_option == "keystore/debug_password" ||
|
||||
p_option == "package/retain_data_on_uninstall" ||
|
||||
p_option == "package/exclude_from_recents" ||
|
||||
p_option == "package/show_in_app_library" ||
|
||||
|
|
@ -2065,6 +2146,9 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor
|
|||
return false;
|
||||
}
|
||||
|
||||
if (p_option == "dotnet/android_use_linux_bionic") {
|
||||
return advanced_options_enabled;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2204,7 +2288,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
|
|||
args.push_back("--user");
|
||||
args.push_back("0");
|
||||
}
|
||||
args.push_back(get_package_name(package_name));
|
||||
args.push_back(get_package_name(p_preset, package_name));
|
||||
|
||||
output.clear();
|
||||
err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
|
||||
|
|
@ -2309,16 +2393,16 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
|
|||
// Going with implicit launch first based on the LAUNCHER category and the app's package.
|
||||
args.push_back("-c");
|
||||
args.push_back("android.intent.category.LAUNCHER");
|
||||
args.push_back(get_package_name(package_name));
|
||||
args.push_back(get_package_name(p_preset, package_name));
|
||||
|
||||
output.clear();
|
||||
err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
|
||||
print_verbose(output);
|
||||
if (err || rv != 0 || output.contains("Error: Activity not started")) {
|
||||
// The implicit launch failed, let's try an explicit launch by specifying the component name before giving up.
|
||||
const String component_name = get_package_name(package_name) + "/com.godot.game.GodotApp";
|
||||
const String component_name = get_package_name(p_preset, package_name) + "/com.godot.game.GodotApp";
|
||||
print_line("Implicit launch failed.. Trying explicit launch using", component_name);
|
||||
args.erase(get_package_name(package_name));
|
||||
args.erase(get_package_name(p_preset, package_name));
|
||||
args.push_back("-n");
|
||||
args.push_back(component_name);
|
||||
|
||||
|
|
@ -2531,38 +2615,52 @@ bool EditorExportPlatformAndroid::has_valid_username_and_password(const Ref<Edit
|
|||
}
|
||||
|
||||
#ifdef MODULE_MONO_ENABLED
|
||||
static uint64_t _last_validate_tfm_time = 0;
|
||||
static String _last_validate_tfm = "";
|
||||
|
||||
bool _validate_dotnet_tfm(const String &required_tfm, String &r_error) {
|
||||
String assembly_name = path::get_csharp_project_name();
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
String project_path = ProjectSettings::get_singleton()->globalize_path("res://" + assembly_name + ".csproj");
|
||||
|
||||
if (!FileAccess::exists(project_path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String pipe;
|
||||
List<String> args;
|
||||
args.push_back("build");
|
||||
args.push_back(project_path);
|
||||
args.push_back("--getProperty:TargetFramework");
|
||||
uint64_t modified_time = FileAccess::get_modified_time(project_path);
|
||||
String tfm;
|
||||
|
||||
int exitcode;
|
||||
Error err = OS::get_singleton()->execute("dotnet", args, &pipe, &exitcode, true);
|
||||
if (err != OK || exitcode != 0) {
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Failed to execute dotnet command. Error " + String(error_names[err]));
|
||||
} else if (exitcode != 0) {
|
||||
print_line(pipe);
|
||||
WARN_PRINT("dotnet command exited with code " + itos(exitcode) + ". See output above for more details.");
|
||||
}
|
||||
r_error += vformat(TTR("Unable to determine the C# project's TFM, it may be incompatible. The export template only supports '%s'. Make sure the project targets '%s' or consider using gradle builds instead."), required_tfm, required_tfm) + "\n";
|
||||
if (modified_time == _last_validate_tfm_time) {
|
||||
tfm = _last_validate_tfm;
|
||||
} else {
|
||||
String tfm = pipe.strip_edges();
|
||||
if (tfm != required_tfm) {
|
||||
r_error += vformat(TTR("C# project targets '%s' but the export template only supports '%s'. Consider using gradle builds instead."), tfm, required_tfm) + "\n";
|
||||
return false;
|
||||
String pipe;
|
||||
List<String> args;
|
||||
args.push_back("build");
|
||||
args.push_back(project_path);
|
||||
args.push_back("--getProperty:TargetFramework");
|
||||
|
||||
int exitcode;
|
||||
Error err = OS::get_singleton()->execute("dotnet", args, &pipe, &exitcode, true);
|
||||
if (err != OK || exitcode != 0) {
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Failed to execute dotnet command. Error " + String(error_names[err]));
|
||||
} else if (exitcode != 0) {
|
||||
print_line(pipe);
|
||||
WARN_PRINT("dotnet command exited with code " + itos(exitcode) + ". See output above for more details.");
|
||||
}
|
||||
r_error += vformat(TTR("Unable to determine the C# project's TFM, it may be incompatible. The export template only supports '%s'. Make sure the project targets '%s' or consider using gradle builds instead."), required_tfm, required_tfm) + "\n";
|
||||
return true;
|
||||
} else {
|
||||
tfm = pipe.strip_edges();
|
||||
_last_validate_tfm_time = modified_time;
|
||||
_last_validate_tfm = tfm;
|
||||
}
|
||||
}
|
||||
|
||||
if (tfm != required_tfm) {
|
||||
r_error += vformat(TTR("C# project targets '%s' but the export template only supports '%s'. Consider using gradle builds instead."), tfm, required_tfm) + "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2599,6 +2697,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
|
|||
if (!dvalid) {
|
||||
template_err += TTR("Custom debug template not found.") + "\n";
|
||||
}
|
||||
has_export_templates |= dvalid;
|
||||
} else {
|
||||
has_export_templates |= exists_export_template("android_debug.apk", &template_err);
|
||||
}
|
||||
|
|
@ -2608,6 +2707,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
|
|||
if (!rvalid) {
|
||||
template_err += TTR("Custom release template not found.") + "\n";
|
||||
}
|
||||
has_export_templates |= rvalid;
|
||||
} else {
|
||||
has_export_templates |= exists_export_template("android_release.apk", &template_err);
|
||||
}
|
||||
|
|
@ -2819,23 +2919,23 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
|
|||
}
|
||||
}
|
||||
|
||||
String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
|
||||
String current_renderer = get_project_setting(p_preset, "rendering/renderer/rendering_method.mobile");
|
||||
if (current_renderer == "forward_plus") {
|
||||
// Warning only, so don't override `valid`.
|
||||
err += vformat(TTR("The \"%s\" renderer is designed for Desktop devices, and is not suitable for Android devices."), current_renderer);
|
||||
err += "\n";
|
||||
}
|
||||
|
||||
if (_uses_vulkan() && min_sdk_int < VULKAN_MIN_SDK_VERSION) {
|
||||
if (_uses_vulkan(p_preset) && min_sdk_int < VULKAN_MIN_SDK_VERSION) {
|
||||
// Warning only, so don't override `valid`.
|
||||
err += vformat(TTR("\"Min SDK\" should be greater or equal to %d for the \"%s\" renderer."), VULKAN_MIN_SDK_VERSION, current_renderer);
|
||||
err += "\n";
|
||||
}
|
||||
|
||||
String package_name = p_preset->get("package/unique_name");
|
||||
if (package_name.contains("$genname") && !is_project_name_valid()) {
|
||||
if (package_name.contains("$genname") && !is_project_name_valid(p_preset)) {
|
||||
// Warning only, so don't override `valid`.
|
||||
err += vformat(TTR("The project name does not meet the requirement for the package name format and will be updated to \"%s\". Please explicitly specify the package name if needed."), get_valid_basename());
|
||||
err += vformat(TTR("The project name does not meet the requirement for the package name format and will be updated to \"%s\". Please explicitly specify the package name if needed."), get_valid_basename(p_preset));
|
||||
err += "\n";
|
||||
}
|
||||
|
||||
|
|
@ -2853,7 +2953,7 @@ List<String> EditorExportPlatformAndroid::get_binary_extensions(const Ref<Editor
|
|||
String EditorExportPlatformAndroid::get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
|
||||
int version_code = p_preset->get("version/code");
|
||||
String package_name = p_preset->get("package/unique_name");
|
||||
String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
|
||||
String apk_file_name = "main." + itos(version_code) + "." + get_package_name(p_preset, package_name) + ".obb";
|
||||
String fullpath = p_path.get_base_dir().path_join(apk_file_name);
|
||||
return fullpath;
|
||||
}
|
||||
|
|
@ -2965,7 +3065,11 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
|
|||
}
|
||||
|
||||
if (!FileAccess::exists(keystore)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not find keystore, unable to export."));
|
||||
if (p_debug) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not find debug keystore, unable to export."));
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not find release keystore, unable to export."));
|
||||
}
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
|
||||
|
|
@ -3151,6 +3255,17 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA
|
|||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformAndroid::_get_deprecated_plugins_names(const Ref<EditorExportPreset> &p_preset) const {
|
||||
Vector<String> names;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset), names);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
String plugins_names = String("|").join(names);
|
||||
return plugins_names;
|
||||
}
|
||||
|
||||
String EditorExportPlatformAndroid::_get_plugins_names(const Ref<EditorExportPreset> &p_preset) const {
|
||||
Vector<String> names;
|
||||
|
||||
|
|
@ -3320,8 +3435,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
print_verbose("Android sdk path: " + sdk_path);
|
||||
|
||||
// TODO: should we use "package/name" or "application/config/name"?
|
||||
String project_name = get_project_name(p_preset->get("package/name"));
|
||||
err = _create_project_name_strings_files(p_preset, project_name, gradle_build_directory); //project name localization.
|
||||
String project_name = get_project_name(p_preset, p_preset->get("package/name"));
|
||||
err = _create_project_name_strings_files(p_preset, project_name, gradle_build_directory, get_project_setting(p_preset, "application/config/name_localized")); //project name localization.
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name."));
|
||||
}
|
||||
|
|
@ -3382,7 +3497,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
String build_path = ProjectSettings::get_singleton()->globalize_path(gradle_build_directory);
|
||||
build_command = build_path.path_join(build_command);
|
||||
|
||||
String package_name = get_package_name(p_preset->get("package/unique_name"));
|
||||
String package_name = get_package_name(p_preset, p_preset->get("package/unique_name"));
|
||||
String version_code = itos(p_preset->get("version/code"));
|
||||
String version_name = p_preset->get_version("version/name");
|
||||
String min_sdk_version = p_preset->get("gradle_build/min_sdk");
|
||||
|
|
@ -3456,7 +3571,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
}
|
||||
|
||||
String addons_directory = ProjectSettings::get_singleton()->globalize_path("res://addons");
|
||||
String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
|
||||
|
||||
cmdline.push_back("-p"); // argument to specify the start directory.
|
||||
cmdline.push_back(build_path); // start directory.
|
||||
|
|
@ -3473,8 +3587,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
|
||||
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
|
||||
cmdline.push_back("-Pcompress_native_libraries=" + compress_native_libraries_flag); // argument to specify whether the build should compress native libraries.
|
||||
cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG));
|
||||
cmdline.push_back("-Pgodot_rendering_method=" + current_renderer);
|
||||
|
||||
// NOTE: The release keystore is not included in the verbose logging
|
||||
// to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
|
||||
|
|
@ -3497,7 +3609,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
debug_keystore = OS::get_singleton()->get_resource_dir().path_join(debug_keystore).simplify_path();
|
||||
}
|
||||
if (!FileAccess::exists(debug_keystore)) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not find keystore, unable to export."));
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not find debug keystore, unable to export."));
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
|
||||
|
|
@ -3513,7 +3625,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
release_keystore = OS::get_singleton()->get_resource_dir().path_join(release_keystore).simplify_path();
|
||||
}
|
||||
if (!FileAccess::exists(release_keystore)) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not find keystore, unable to export."));
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not find release keystore, unable to export."));
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
|
||||
|
|
@ -3655,6 +3767,17 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||
//write
|
||||
if (file == "AndroidManifest.xml") {
|
||||
_fix_manifest(p_preset, data, p_give_internet);
|
||||
|
||||
// Allow editor export plugins to update the prebuilt manifest as needed.
|
||||
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(Ref<EditorExportPlatform>(this))) {
|
||||
PackedByteArray export_plugin_data = export_plugins[i]->update_android_prebuilt_manifest(Ref<EditorExportPlatform>(this), data);
|
||||
if (!export_plugin_data.is_empty()) {
|
||||
data = export_plugin_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (file == "resources.arsc") {
|
||||
_fix_resources(p_preset, data);
|
||||
|
|
|
|||
|
|
@ -28,13 +28,14 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_EXPORT_PLUGIN_H
|
||||
#define ANDROID_EXPORT_PLUGIN_H
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "godot_plugin_config.h"
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
|
|
@ -76,6 +77,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
EditorProgress *ep = nullptr;
|
||||
};
|
||||
|
||||
struct FeatureInfo {
|
||||
String name;
|
||||
bool required;
|
||||
String version;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
mutable Vector<PluginConfigAndroid> android_plugins;
|
||||
mutable SafeFlag android_plugins_changed;
|
||||
|
|
@ -97,16 +104,16 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
void _update_preset_status();
|
||||
#endif
|
||||
|
||||
String get_project_name(const String &p_name) const;
|
||||
String get_project_name(const Ref<EditorExportPreset> &p_preset, const String &p_name) const;
|
||||
|
||||
String get_package_name(const String &p_package) const;
|
||||
String get_package_name(const Ref<EditorExportPreset> &p_preset, const String &p_package) const;
|
||||
|
||||
String get_valid_basename() const;
|
||||
String get_valid_basename(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
|
||||
|
||||
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
|
||||
bool is_project_name_valid() const;
|
||||
bool is_package_name_valid(const Ref<EditorExportPreset> &p_preset, const String &p_package, String *r_error = nullptr) const;
|
||||
bool is_project_name_valid(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data);
|
||||
|
||||
|
|
@ -152,7 +159,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
|
||||
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 _get_manifest_info(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions, Vector<FeatureInfo> &r_features, Vector<MetadataInfo> &r_metadata);
|
||||
|
||||
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
|
||||
|
||||
|
|
@ -182,7 +189,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
|
||||
static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
static bool _uses_vulkan();
|
||||
bool _uses_vulkan(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
@ -236,6 +243,8 @@ public:
|
|||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
|
||||
String _get_deprecated_plugins_names(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String _get_plugins_names(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
String _resolve_export_plugin_android_library_path(const String &p_android_library_path) const;
|
||||
|
|
@ -269,5 +278,3 @@ public:
|
|||
|
||||
~EditorExportPlatformAndroid();
|
||||
};
|
||||
|
||||
#endif // ANDROID_EXPORT_PLUGIN_H
|
||||
|
|
|
|||
|
|
@ -28,13 +28,11 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_GODOT_PLUGIN_CONFIG_H
|
||||
#define ANDROID_GODOT_PLUGIN_CONFIG_H
|
||||
#pragma once
|
||||
|
||||
#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"
|
||||
|
||||
|
|
@ -104,5 +102,3 @@ struct PluginConfigAndroid {
|
|||
};
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#endif // ANDROID_GODOT_PLUGIN_CONFIG_H
|
||||
|
|
|
|||
|
|
@ -171,8 +171,14 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
|
|||
// This method will be called ONLY when gradle build is enabled.
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) {
|
||||
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
|
||||
const String path = ResourceUID::ensure_path(p_path);
|
||||
|
||||
String path = p_path.simplify_path();
|
||||
if (path.begins_with("uid://")) {
|
||||
path = ResourceUID::uid_to_path(path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, path));
|
||||
}
|
||||
const String dst_path = path.replace_first("res://", export_data->assets_directory + "/");
|
||||
|
||||
print_verbose("Saving project files from " + path + " into " + dst_path);
|
||||
Error err = store_file_at_path(dst_path, p_data);
|
||||
return err;
|
||||
|
|
@ -194,10 +200,10 @@ String _android_xml_escape(const String &p_string) {
|
|||
}
|
||||
|
||||
// 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);
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames) {
|
||||
print_verbose("Creating strings resources for supported locales for project " + p_project_name);
|
||||
// Stores the string into the default values directory.
|
||||
String processed_default_xml_string = vformat(GODOT_PROJECT_NAME_XML_STRING, _android_xml_escape(project_name));
|
||||
String processed_default_xml_string = vformat(GODOT_PROJECT_NAME_XML_STRING, _android_xml_escape(p_project_name));
|
||||
store_string_at_path(p_gradle_build_dir.path_join("res/values/godot_project_name_string.xml"), processed_default_xml_string);
|
||||
|
||||
// Searches the Gradle project res/ directory to find all supported locales
|
||||
|
|
@ -209,7 +215,6 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
|
|||
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()) {
|
||||
|
|
@ -221,8 +226,8 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
|
|||
}
|
||||
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];
|
||||
if (p_appnames.has(locale)) {
|
||||
String locale_project_name = p_appnames[locale];
|
||||
String processed_xml_string = vformat(GODOT_PROJECT_NAME_XML_STRING, _android_xml_escape(locale_project_name));
|
||||
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
|
||||
store_string_at_path(locale_directory, processed_xml_string);
|
||||
|
|
@ -246,7 +251,7 @@ String _get_gles_tag() {
|
|||
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]);
|
||||
constexpr size_t num_sizes = std::size(sizes);
|
||||
for (size_t i = 0; i < num_sizes; i++) {
|
||||
String feature_name = vformat("screen/support_%s", sizes[i]);
|
||||
String feature_support = bool_to_string(p_preset->get(feature_name));
|
||||
|
|
@ -258,7 +263,7 @@ 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 orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
|
||||
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(p_export_platform->get_project_setting(p_preset, "display/window/handheld/orientation"))));
|
||||
String manifest_activity_text = vformat(
|
||||
" <activity android:name=\".GodotApp\" "
|
||||
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
|
||||
|
|
@ -268,7 +273,7 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
|
|||
"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"))));
|
||||
bool_to_string(bool(p_export_platform->get_project_setting(p_preset, "display/window/size/resizable"))));
|
||||
|
||||
manifest_activity_text += " <intent-filter>\n"
|
||||
" <action android:name=\"android.intent.action.MAIN\" />\n"
|
||||
|
|
@ -306,7 +311,7 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
|
|||
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) {
|
||||
String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug, const Vector<MetadataInfo> &p_metadata) {
|
||||
int app_category_index = (int)(p_preset->get("package/app_category"));
|
||||
bool is_game = app_category_index == APP_CATEGORY_GAME;
|
||||
|
||||
|
|
@ -330,6 +335,10 @@ String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform,
|
|||
}
|
||||
manifest_application_text += " tools:ignore=\"GoogleAppIndexingWarning\">\n\n";
|
||||
|
||||
for (int i = 0; i < p_metadata.size(); i++) {
|
||||
manifest_application_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"%s\" android:value=\"%s\" />\n", p_metadata[i].name, p_metadata[i].value);
|
||||
}
|
||||
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (export_plugins[i]->supports_platform(p_export_platform)) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANDROID_GRADLE_EXPORT_UTIL_H
|
||||
#define ANDROID_GRADLE_EXPORT_UTIL_H
|
||||
#pragma once
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
|
@ -69,6 +68,11 @@ struct CustomExportData {
|
|||
Vector<String> libs;
|
||||
};
|
||||
|
||||
struct MetadataInfo {
|
||||
String name;
|
||||
String value;
|
||||
};
|
||||
|
||||
int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation);
|
||||
|
||||
String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation);
|
||||
|
|
@ -96,7 +100,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
|
|||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
|
||||
// Creates strings.xml files inside the gradle project for different locales.
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir);
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames);
|
||||
|
||||
String bool_to_string(bool v);
|
||||
|
||||
|
|
@ -106,6 +110,4 @@ 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
|
||||
String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug, const Vector<MetadataInfo> &p_metadata);
|
||||
|
|
|
|||
|
|
@ -1 +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>
|
||||
<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>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 422 B |
|
|
@ -1 +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>
|
||||
<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>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 432 B |
|
|
@ -53,9 +53,9 @@ Error FileAccessAndroid::open_internal(const String &p_path, int p_mode_flags) {
|
|||
String path = fix_path(p_path).simplify_path();
|
||||
absolute_path = path;
|
||||
if (path.begins_with("/")) {
|
||||
path = path.substr(1, path.length());
|
||||
path = path.substr(1);
|
||||
} else if (path.begins_with("res://")) {
|
||||
path = path.substr(6, path.length());
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, ERR_UNAVAILABLE); //can't write on android..
|
||||
|
|
@ -132,6 +132,10 @@ uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const
|
|||
return r;
|
||||
}
|
||||
|
||||
int64_t FileAccessAndroid::_get_size(const String &p_file) {
|
||||
return AAsset_getLength64(asset);
|
||||
}
|
||||
|
||||
Error FileAccessAndroid::get_error() const {
|
||||
return eof ? ERR_FILE_EOF : OK; // not sure what else it may happen
|
||||
}
|
||||
|
|
@ -147,9 +151,9 @@ bool FileAccessAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
|||
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());
|
||||
path = path.substr(1);
|
||||
} else if (path.begins_with("res://")) {
|
||||
path = path.substr(6, path.length());
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
AAsset *at = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FILE_ACCESS_ANDROID_H
|
||||
#define FILE_ACCESS_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
|
|
@ -39,6 +38,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
class FileAccessAndroid : public FileAccess {
|
||||
GDSOFTCLASS(FileAccessAndroid, FileAccess);
|
||||
static AAssetManager *asset_manager;
|
||||
static jobject j_asset_manager;
|
||||
|
||||
|
|
@ -78,6 +78,8 @@ public:
|
|||
virtual bool file_exists(const String &p_path) override; // return true if a file exists
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
|
||||
virtual uint64_t _get_access_time(const String &p_file) override { return 0; }
|
||||
virtual int64_t _get_size(const String &p_file) override;
|
||||
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
|
|
@ -94,5 +96,3 @@ public:
|
|||
|
||||
~FileAccessAndroid();
|
||||
};
|
||||
|
||||
#endif // FILE_ACCESS_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
|
|||
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_exists = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_last_modified = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_last_accessed = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_resize = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_size = nullptr;
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_path() const {
|
||||
return path_src;
|
||||
|
|
@ -201,7 +203,7 @@ String FileAccessFilesystemJAndroid::get_line() const {
|
|||
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)) {
|
||||
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
|
|
@ -209,7 +211,7 @@ String FileAccessFilesystemJAndroid::get_line() const {
|
|||
}
|
||||
}
|
||||
|
||||
if (result.parse_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
|
|
@ -300,7 +302,7 @@ bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) {
|
|||
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);
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
|
|
@ -312,6 +314,36 @@ uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file)
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::_get_access_time(const String &p_file) {
|
||||
if (_file_last_accessed) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
uint64_t result = env->CallLongMethod(file_access_handler, _file_last_accessed, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FileAccessFilesystemJAndroid::_get_size(const String &p_file) {
|
||||
if (_file_size) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, -1);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int64_t result = env->CallLongMethod(file_access_handler, _file_size, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
file_access_handler = env->NewGlobalRef(p_file_access_handler);
|
||||
|
|
@ -332,7 +364,9 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
|
|||
_file_flush = env->GetMethodID(cls, "fileFlush", "(I)V");
|
||||
_file_exists = env->GetMethodID(cls, "fileExists", "(Ljava/lang/String;)Z");
|
||||
_file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J");
|
||||
_file_last_accessed = env->GetMethodID(cls, "fileLastAccessed", "(Ljava/lang/String;)J");
|
||||
_file_resize = env->GetMethodID(cls, "fileResize", "(IJ)I");
|
||||
_file_size = env->GetMethodID(cls, "fileSize", "(Ljava/lang/String;)J");
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::terminate() {
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FILE_ACCESS_FILESYSTEM_JANDROID_H
|
||||
#define FILE_ACCESS_FILESYSTEM_JANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "java_godot_lib_jni.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
class FileAccessFilesystemJAndroid : public FileAccess {
|
||||
GDSOFTCLASS(FileAccessFilesystemJAndroid, FileAccess);
|
||||
static jobject file_access_handler;
|
||||
static jclass cls;
|
||||
|
||||
|
|
@ -52,7 +52,9 @@ class FileAccessFilesystemJAndroid : public FileAccess {
|
|||
static jmethodID _file_close;
|
||||
static jmethodID _file_exists;
|
||||
static jmethodID _file_last_modified;
|
||||
static jmethodID _file_last_accessed;
|
||||
static jmethodID _file_resize;
|
||||
static jmethodID _file_size;
|
||||
|
||||
int id;
|
||||
String absolute_path;
|
||||
|
|
@ -92,6 +94,8 @@ public:
|
|||
static void terminate();
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file) override;
|
||||
virtual uint64_t _get_access_time(const String &p_file) override;
|
||||
virtual int64_t _get_size(const String &p_file) override;
|
||||
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||
|
||||
|
|
@ -105,5 +109,3 @@ public:
|
|||
FileAccessFilesystemJAndroid();
|
||||
~FileAccessFilesystemJAndroid();
|
||||
};
|
||||
|
||||
#endif // FILE_ACCESS_FILESYSTEM_JANDROID_H
|
||||
|
|
|
|||
|
|
@ -133,4 +133,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_playMainSc
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||
game_view_plugin->get_debugger()->set_debug_mute_audio(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GAME_MENU_UTILS_JNI_H
|
||||
#define GAME_MENU_UTILS_JNI_H
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
|
@ -44,6 +43,5 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_setCameraM
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_resetCamera2DPosition(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled);
|
||||
}
|
||||
|
||||
#endif // GAME_MENU_UTILS_JNI_H
|
||||
|
|
|
|||
|
|
@ -29,15 +29,6 @@
|
|||
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}" />
|
||||
<!-- Records the rendering method used by the Godot engine -->
|
||||
<meta-data
|
||||
android:name="org.godotengine.rendering.method"
|
||||
android:value="${godotRenderingMethod}"/>
|
||||
|
||||
<activity
|
||||
android:name=".GodotApp"
|
||||
android:label="@string/godot_project_name_string"
|
||||
|
|
@ -46,7 +37,7 @@
|
|||
android:excludeFromRecents="false"
|
||||
android:exported="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:resizeableActivity="false"
|
||||
tools:ignore="UnusedAttribute" >
|
||||
|
||||
|
|
|
|||
|
|
@ -105,11 +105,6 @@ android {
|
|||
abiFilters export_abi_list
|
||||
}
|
||||
|
||||
manifestPlaceholders = [
|
||||
godotEditorVersion: getGodotEditorVersion(),
|
||||
godotRenderingMethod: getGodotRenderingMethod()
|
||||
]
|
||||
|
||||
// Feel free to modify the application id to your own.
|
||||
applicationId getExportPackageName()
|
||||
versionCode getExportVersionCode()
|
||||
|
|
|
|||
|
|
@ -71,25 +71,6 @@ ext.getExportTargetSdkVersion = { ->
|
|||
}
|
||||
}
|
||||
|
||||
ext.getGodotRenderingMethod = { ->
|
||||
String renderingMethod = project.hasProperty("godot_rendering_method") ? project.property("godot_rendering_method") : ""
|
||||
return renderingMethod
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@
|
|||
<!-- Enable passthrough background during the splash screen -->
|
||||
<meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
|
||||
|
||||
<meta-data android:name="com.oculus.handtracking.version" android:value="V2.0" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<activity
|
||||
android:name=".GodotEditor"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="true"
|
||||
android:icon="@mipmap/themed_icon"
|
||||
android:launchMode="singleTask"
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
</activity>
|
||||
<activity
|
||||
android:name=".GodotGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="false"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
</activity>
|
||||
<activity
|
||||
android:name=".embed.EmbeddedGodotGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:exported="false"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
android:screenOrientation="userLandscape" />
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":GodotXRGame"
|
||||
android:launchMode="singleTask"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@ import android.content.ComponentName
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.*
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Debug
|
||||
import android.os.Environment
|
||||
import android.os.Process
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
|
|
@ -138,6 +142,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||
internal const val GAME_MENU_ACTION_RESET_CAMERA_2D_POSITION = "resetCamera2DPosition"
|
||||
internal const val GAME_MENU_ACTION_RESET_CAMERA_3D_POSITION = "resetCamera3DPosition"
|
||||
internal const val GAME_MENU_ACTION_EMBED_GAME_ON_PLAY = "embedGameOnPlay"
|
||||
internal const val GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO = "setDebugMuteAudio"
|
||||
|
||||
private const val GAME_WORKSPACE = "Game"
|
||||
|
||||
|
|
@ -258,12 +263,14 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||
super.onGodotSetupCompleted()
|
||||
val longPressEnabled = enableLongPressGestures()
|
||||
val panScaleEnabled = enablePanAndScaleGestures()
|
||||
val overrideVolumeButtonsEnabled = overrideVolumeButtons()
|
||||
|
||||
runOnUiThread {
|
||||
// Enable long press, panning and scaling gestures
|
||||
godotFragment?.godot?.renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
setOverrideVolumeButtons(overrideVolumeButtonsEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -482,12 +489,19 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||
*/
|
||||
protected open fun overrideOrientationRequest() = true
|
||||
|
||||
protected open fun overrideVolumeButtons() = false
|
||||
|
||||
/**
|
||||
* 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"))
|
||||
|
||||
/**
|
||||
* Disable scroll deadzone for the Godot Android editor.
|
||||
*/
|
||||
protected open fun disableScrollDeadzone() = true
|
||||
|
||||
/**
|
||||
* Enable pan and scale gestures for the Godot Android editor.
|
||||
*/
|
||||
|
|
@ -749,6 +763,10 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||
val embedded = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
|
||||
embedGameOnPlay(embedded)
|
||||
}
|
||||
GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO -> {
|
||||
val enabled = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
|
||||
muteAudio(enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -812,6 +830,13 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||
}
|
||||
}
|
||||
|
||||
override fun muteAudio(enabled: Boolean) {
|
||||
gameMenuState.putBoolean(GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO, enabled)
|
||||
godot?.runOnRenderThread {
|
||||
GameMenuUtils.setDebugMuteAudio(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun embedGameOnPlay(embedded: Boolean) {
|
||||
gameMenuState.putBoolean(GAME_MENU_ACTION_EMBED_GAME_ON_PLAY, embedded)
|
||||
godot?.runOnRenderThread {
|
||||
|
|
|
|||
|
|
@ -46,10 +46,14 @@ abstract class BaseGodotGame: GodotEditor() {
|
|||
private val TAG = BaseGodotGame::class.java.simpleName
|
||||
}
|
||||
|
||||
override fun overrideVolumeButtons() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/override_volume_buttons"))
|
||||
|
||||
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 disableScrollDeadzone() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/disable_scroll_deadzone"))
|
||||
|
||||
override fun onGodotSetupCompleted() {
|
||||
super.onGodotSetupCompleted()
|
||||
Log.v(TAG, "OnGodotSetupCompleted")
|
||||
|
|
|
|||
|
|
@ -206,6 +206,14 @@ open class GodotGame : BaseGodotGame() {
|
|||
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
||||
}
|
||||
|
||||
override fun muteAudio(enabled: Boolean) {
|
||||
val actionBundle = Bundle().apply {
|
||||
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO)
|
||||
putBoolean(KEY_GAME_MENU_ACTION_PARAM1, enabled)
|
||||
}
|
||||
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
||||
}
|
||||
|
||||
override fun embedGameOnPlay(embedded: Boolean) {
|
||||
val actionBundle = Bundle().apply {
|
||||
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_EMBED_GAME_ON_PLAY)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||
fun reset2DCamera()
|
||||
fun reset3DCamera()
|
||||
fun manipulateCamera(mode: CameraMode)
|
||||
fun muteAudio(enabled: Boolean)
|
||||
|
||||
fun isGameEmbeddingSupported(): Boolean
|
||||
fun embedGameOnPlay(embedded: Boolean)
|
||||
|
|
@ -148,6 +149,9 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||
private val listSelectButton: RadioButton? by lazy {
|
||||
view?.findViewById(R.id.game_menu_list_select_button)
|
||||
}
|
||||
private val audioMuteButton: View? by lazy {
|
||||
view?.findViewById(R.id.game_menu_audio_mute_button)
|
||||
}
|
||||
private val optionsButton: View? by lazy {
|
||||
view?.findViewById(R.id.game_menu_options_button)
|
||||
}
|
||||
|
|
@ -319,6 +323,13 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
audioMuteButton?.apply{
|
||||
setOnClickListener {
|
||||
val isActivated = !it.isActivated
|
||||
menuListener?.muteAudio(isActivated)
|
||||
it.isActivated = isActivated
|
||||
}
|
||||
}
|
||||
optionsButton?.setOnClickListener {
|
||||
popupMenu.show()
|
||||
}
|
||||
|
|
@ -351,6 +362,8 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||
toolSelectButton?.isChecked = selectMode == GameMenuListener.SelectMode.SINGLE
|
||||
listSelectButton?.isChecked = selectMode == GameMenuListener.SelectMode.LIST
|
||||
|
||||
audioMuteButton?.isActivated = gameMenuState.getBoolean(BaseGodotEditor.GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO, false)
|
||||
|
||||
popupMenu.menu.apply {
|
||||
if (menuListener?.isGameEmbeddingSupported() == false) {
|
||||
setGroupEnabled(R.id.group_menu_embed_options, false)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="2.4"
|
||||
android:viewportHeight="2.4">
|
||||
|
||||
<path
|
||||
android:pathData="M1.252 0.15 a0.1 0.1 0 0 0-0.082 0.03 L0.6 0.75 H0.318C0.225 0.75 0.15 0.817 0.15 0.9 v0.6c0 0.083 0.075 0.15 0.168 0.15H0.6l0.57 0.57 c0.066 0.067 0.18 0.02 0.18-0.074V0.256A0.106 0.106 0 0 0 1.252 0.15"
|
||||
android:fillColor="@color/game_menu_icons_color_state"/>
|
||||
<path
|
||||
android:strokeWidth=".165"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M1.575 0.675 c0.45 0.525 0 1.05 0 1.05m0.3-1.35c0.675 0.825 0 1.65 0 1.65"
|
||||
android:strokeColor="@color/game_menu_icons_color_state"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/audio_player_muted" android:state_activated="true" />
|
||||
<item android:drawable="@drawable/audio_player" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="2.4"
|
||||
android:viewportHeight="2.4">
|
||||
|
||||
<path
|
||||
android:pathData="M 1.2518555 0.15 A 0.1 0.1 0 0 0 1.1701172 0.17988281 L 0.6 0.75 L 0.31787109 0.75 C 0.22487119 0.75 0.15 0.81700008 0.15 0.9 L 0.15 1.5 C 0.15 1.5424221 0.16969512 1.5805593 0.20126953 1.6078125 L 1.35 0.45527344 L 1.35 0.25605469 A 0.106 0.106 0 0 0 1.2518555 0.15 z M 1.35 1.6438477 L 0.97236328 2.0223633 L 1.1701172 2.2201172 C 1.2361171 2.2871171 1.35 2.239996 1.35 2.1459961 L 1.35 1.6438477 z"
|
||||
android:fillColor="@color/game_menu_icons_color_state"/>
|
||||
<path
|
||||
android:pathData="M 2.1984375 0.79306641 L 2.0660156 0.92578125 C 2.1142629 1.1320246 2.0935608 1.3239034 2.0487305 1.4882812 C 1.9692536 1.7796963 1.8105469 1.9725586 1.8105469 1.9725586 A 0.0825 0.0825 0 0 0 1.8222656 2.0879883 A 0.0825 0.0825 0 0 0 1.9394531 2.0780273 C 1.9394531 2.0780273 2.1176607 1.8586814 2.2069336 1.5313477 C 2.2638362 1.3227069 2.2834498 1.0648044 2.1984375 0.79306641 z M 1.8539062 1.1384766 L 1.6790039 1.3136719 C 1.6747238 1.3346601 1.6697313 1.3550615 1.6640625 1.3749023 C 1.6131343 1.5531513 1.5117188 1.6719727 1.5117187 1.6719727 A 0.0825 0.0825 0 0 0 1.5213867 1.7871094 A 0.0825 0.0825 0 0 0 1.6368164 1.7791992 C 1.6368164 1.7791992 1.7606941 1.6355198 1.8222656 1.4200195 C 1.8460259 1.3368593 1.8597024 1.2410136 1.8539062 1.1384766 z"
|
||||
android:fillColor="@color/game_menu_icons_color_state"/>
|
||||
<path
|
||||
android:pathData="M0.08295 2.0529 2.0502 0.07965 2.31705 0.34725 0.34965 2.32035ZM-1.2804596 3.0939027 3.0879072-1.2877874Z"
|
||||
android:fillColor="#fc7f7f"/>
|
||||
</vector>
|
||||
|
|
@ -128,6 +128,15 @@
|
|||
android:drawableStart="@drawable/list_select"
|
||||
android:padding="15dp" />
|
||||
</RadioGroup>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/game_menu_audio_mute_button"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/game_menu_button_bg"
|
||||
android:src="@drawable/audio_player_icon_selector" />
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ import org.godotengine.godot.xr.XRMode
|
|||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.lang.Exception
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
|
@ -714,11 +713,15 @@ class Godot(private val context: Context) {
|
|||
val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
|
||||
val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
||||
val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
|
||||
val overrideVolumeButtons = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/override_volume_buttons"))
|
||||
val scrollDeadzoneDisabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/disable_scroll_deadzone"))
|
||||
|
||||
runOnUiThread {
|
||||
renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
setOverrideVolumeButtons(overrideVolumeButtons)
|
||||
disableScrollDeadzone(scrollDeadzoneDisabled)
|
||||
try {
|
||||
setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
|
||||
} catch (e: NumberFormatException) {
|
||||
|
|
|
|||
|
|
@ -268,6 +268,11 @@ public class GodotLib {
|
|||
*/
|
||||
public static native void onNightModeChanged();
|
||||
|
||||
/**
|
||||
* Invoked on the hardware keyboard connected/disconnected.
|
||||
*/
|
||||
public static native void hardwareKeyboardConnected(boolean connected);
|
||||
|
||||
/**
|
||||
* Invoked on the file picker closed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -265,13 +265,6 @@ public class GodotEditText extends EditText {
|
|||
}
|
||||
|
||||
public boolean hasHardwareKeyboard() {
|
||||
Configuration config = getResources().getConfiguration();
|
||||
boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS &&
|
||||
config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
|
||||
if (hasHardwareKeyboardConfig) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mRenderView.getInputHandler().hasHardwareKeyboard();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
|
|||
*/
|
||||
var panningAndScalingEnabled = false
|
||||
|
||||
var scrollDeadzoneDisabled = false
|
||||
|
||||
private var nextDownIsDoubleTap = false
|
||||
private var dragInProgress = false
|
||||
private var scaleInProgress = false
|
||||
|
|
@ -153,7 +155,7 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
|
|||
if (contextClickInProgress) {
|
||||
inputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false)
|
||||
return true
|
||||
} else if (!scaleInProgress) {
|
||||
} else if (scrollDeadzoneDisabled && !scaleInProgress) {
|
||||
// The 'onScroll' event is triggered with a long delay.
|
||||
// Force the 'InputEventScreenDrag' event earlier here.
|
||||
// We don't toggle 'dragInProgress' here so that the scaling logic can override the drag operation if needed.
|
||||
|
|
@ -191,7 +193,7 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
|
|||
distanceY: Float
|
||||
): Boolean {
|
||||
if (scaleInProgress) {
|
||||
if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) {
|
||||
if (dragInProgress || (scrollDeadzoneDisabled && (lastDragX != 0.0f || lastDragY != 0.0f))) {
|
||||
if (originEvent != null) {
|
||||
// Cancel the drag
|
||||
inputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import androidx.annotation.NonNull;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Handles input related events for the {@link GodotRenderView} view.
|
||||
|
|
@ -83,11 +84,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
/**
|
||||
* Used to decide whether mouse capture can be enabled.
|
||||
*/
|
||||
private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
|
||||
private AtomicInteger lastSeenToolType = new AtomicInteger(MotionEvent.TOOL_TYPE_UNKNOWN);
|
||||
|
||||
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
|
||||
|
||||
private int cachedRotation = -1;
|
||||
private boolean overrideVolumeButtons = false;
|
||||
private boolean hasHardwareKeyboardConfig = false;
|
||||
|
||||
public GodotInputHandler(Context context, Godot godot) {
|
||||
this.godot = godot;
|
||||
|
|
@ -103,6 +106,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
this.scaleGestureDetector.setStylusScaleEnabled(true);
|
||||
}
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS &&
|
||||
config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,6 +118,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
this.gestureDetector.setIsLongpressEnabled(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable scroll deadzone. This is false by default.
|
||||
*/
|
||||
public void disableScrollDeadzone(boolean disable) {
|
||||
this.godotGestureHandler.setScrollDeadzoneDisabled(disable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable multi-fingers pan & scale gestures. This is false by default.
|
||||
* <p>
|
||||
|
|
@ -136,7 +149,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
rotaryInputAxis = axis;
|
||||
}
|
||||
|
||||
public void setOverrideVolumeButtons(boolean value) {
|
||||
overrideVolumeButtons = value;
|
||||
}
|
||||
|
||||
boolean hasHardwareKeyboard() {
|
||||
if (hasHardwareKeyboardConfig) {
|
||||
return true;
|
||||
}
|
||||
return !mHardwareKeyboardIds.isEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +169,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
}
|
||||
|
||||
public boolean canCapturePointer() {
|
||||
return lastSeenToolType == MotionEvent.TOOL_TYPE_MOUSE;
|
||||
return lastSeenToolType.get() == MotionEvent.TOOL_TYPE_MOUSE ||
|
||||
lastSeenToolType.get() == MotionEvent.TOOL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public void onPointerCaptureChange(boolean hasCapture) {
|
||||
|
|
@ -157,10 +178,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
}
|
||||
|
||||
public boolean onKeyUp(final int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int source = event.getSource();
|
||||
if (isKeyEventGameDevice(source)) {
|
||||
// Check if the device exists
|
||||
|
|
@ -178,14 +195,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
handleKeyEvent(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
|
||||
};
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
return overrideVolumeButtons;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onKeyDown(final int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int source = event.getSource();
|
||||
|
||||
final int deviceId = event.getDeviceId();
|
||||
|
|
@ -206,11 +223,15 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
handleKeyEvent(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
return overrideVolumeButtons;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(final MotionEvent event) {
|
||||
lastSeenToolType = getEventToolType(event);
|
||||
lastSeenToolType.set(getEventToolType(event));
|
||||
|
||||
this.scaleGestureDetector.onTouchEvent(event);
|
||||
if (this.gestureDetector.onTouchEvent(event)) {
|
||||
|
|
@ -236,7 +257,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
}
|
||||
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
lastSeenToolType = getEventToolType(event);
|
||||
lastSeenToolType.set(getEventToolType(event));
|
||||
|
||||
if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
|
||||
// Check if the device exists
|
||||
|
|
@ -790,5 +811,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
|
|||
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
updateCachedRotation();
|
||||
|
||||
boolean newHardwareKeyboardConfig = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS &&
|
||||
newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
|
||||
if (hasHardwareKeyboardConfig != newHardwareKeyboardConfig) {
|
||||
hasHardwareKeyboardConfig = newHardwareKeyboardConfig;
|
||||
GodotLib.hardwareKeyboardConnected(hasHardwareKeyboard());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ internal class AssetData(context: Context, private val filePath: String, accessF
|
|||
}
|
||||
|
||||
fun fileLastModified(path: String) = 0L
|
||||
fun fileLastAccessed(path: String) = 0L
|
||||
fun fileSize(path: String) = -1L
|
||||
|
||||
fun delete(path: String) = false
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,24 @@ internal abstract class DataAccess {
|
|||
}
|
||||
}
|
||||
|
||||
fun fileLastAccessed(storageScope: StorageScope, context: Context, path: String): Long {
|
||||
return when(storageScope) {
|
||||
StorageScope.APP -> FileData.fileLastAccessed(path)
|
||||
StorageScope.ASSETS -> AssetData.fileLastAccessed(path)
|
||||
StorageScope.SHARED -> MediaStoreData.fileLastAccessed(context, path)
|
||||
StorageScope.UNKNOWN -> 0L
|
||||
}
|
||||
}
|
||||
|
||||
fun fileSize(storageScope: StorageScope, context: Context, path: String): Long {
|
||||
return when(storageScope) {
|
||||
StorageScope.APP -> FileData.fileSize(path)
|
||||
StorageScope.ASSETS -> AssetData.fileSize(path)
|
||||
StorageScope.SHARED -> MediaStoreData.fileSize(context, path)
|
||||
StorageScope.UNKNOWN -> -1L
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFile(storageScope: StorageScope, context: Context, path: String): Boolean {
|
||||
return when(storageScope) {
|
||||
StorageScope.APP -> FileData.delete(path)
|
||||
|
|
|
|||
|
|
@ -228,6 +228,21 @@ class FileAccessHandler(val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun fileLastAccessed(filepath: String?): Long {
|
||||
val storageScope = storageScopeIdentifier.identifyStorageScope(filepath)
|
||||
if (storageScope == StorageScope.UNKNOWN) {
|
||||
return 0L
|
||||
}
|
||||
|
||||
return try {
|
||||
filepath?.let {
|
||||
DataAccess.fileLastAccessed(storageScope, context, it)
|
||||
} ?: 0L
|
||||
} catch (e: SecurityException) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
fun fileResize(fileId: Int, length: Long): Int {
|
||||
if (!hasFileId(fileId)) {
|
||||
return Error.FAILED.toNativeValue()
|
||||
|
|
@ -236,6 +251,21 @@ class FileAccessHandler(val context: Context) {
|
|||
return files[fileId].resize(length).toNativeValue()
|
||||
}
|
||||
|
||||
fun fileSize(filepath: String?): Long {
|
||||
val storageScope = storageScopeIdentifier.identifyStorageScope(filepath)
|
||||
if (storageScope == StorageScope.UNKNOWN) {
|
||||
return -1L
|
||||
}
|
||||
|
||||
return try {
|
||||
filepath?.let {
|
||||
DataAccess.fileSize(storageScope, context, it)
|
||||
} ?: -1L
|
||||
} catch (e: SecurityException) {
|
||||
-1L
|
||||
}
|
||||
}
|
||||
|
||||
fun fileGetPosition(fileId: Int): Long {
|
||||
if (!hasFileId(fileId)) {
|
||||
return 0L
|
||||
|
|
|
|||
|
|
@ -30,10 +30,16 @@
|
|||
|
||||
package org.godotengine.godot.io.file
|
||||
|
||||
import android.os.*
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Implementation of [DataAccess] which handles regular (not scoped) file access and interactions.
|
||||
|
|
@ -59,6 +65,30 @@ internal class FileData(filePath: String, accessFlag: FileAccessFlags) : DataAcc
|
|||
}
|
||||
}
|
||||
|
||||
fun fileLastAccessed(filepath: String): Long {
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Files.readAttributes<BasicFileAttributes>(FileSystems.getDefault().getPath(filepath), BasicFileAttributes::class.java).lastAccessTime().to(TimeUnit.SECONDS)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
fun fileSize(filepath: String): Long {
|
||||
return try {
|
||||
if (File(filepath).isFile) {
|
||||
File(filepath).length()
|
||||
} else {
|
||||
-1L
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
-1L
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(filepath: String): Boolean {
|
||||
return try {
|
||||
File(filepath).delete()
|
||||
|
|
|
|||
|
|
@ -212,6 +212,20 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi
|
|||
return dataItem.dateModified.toLong() / 1000L
|
||||
}
|
||||
|
||||
fun fileLastAccessed(@Suppress("UNUSED_PARAMETER") context: Context, @Suppress("UNUSED_PARAMETER") path: String): Long {
|
||||
return 0L
|
||||
}
|
||||
|
||||
fun fileSize(context: Context, path: String): Long {
|
||||
val result = queryByPath(context, path)
|
||||
if (result.isEmpty()) {
|
||||
return -1L
|
||||
}
|
||||
|
||||
val dataItem = result[0]
|
||||
return dataItem.size.toLong()
|
||||
}
|
||||
|
||||
fun rename(context: Context, from: String, to: String): Boolean {
|
||||
// Ensure the source exists.
|
||||
val sources = queryByPath(context, from)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ object GameMenuUtils {
|
|||
@JvmStatic
|
||||
external fun playMainScene()
|
||||
|
||||
@JvmStatic
|
||||
external fun setDebugMuteAudio(enabled: Boolean)
|
||||
|
||||
/**
|
||||
* Returns [GameEmbedMode] stored in the editor settings.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -108,18 +108,21 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
|||
}
|
||||
} break;
|
||||
case ARG_TYPE_CLASS: {
|
||||
if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
|
||||
String cn = E.param_sigs[i].operator String();
|
||||
if (cn.begins_with("L") && cn.ends_with(";")) {
|
||||
cn = cn.substr(1, cn.length() - 2);
|
||||
}
|
||||
if (cn == "org/godotengine/godot/Dictionary") {
|
||||
if (p_args[i]->get_type() != Variant::DICTIONARY) {
|
||||
arg_expected = Variant::DICTIONARY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
|
||||
arg_expected = Variant::OBJECT;
|
||||
} else {
|
||||
Ref<RefCounted> ref = *p_args[i];
|
||||
if (ref.is_valid()) {
|
||||
if (Object::cast_to<JavaObject>(ref.ptr())) {
|
||||
Ref<JavaObject> jo = ref;
|
||||
//could be faster
|
||||
String cn = E.param_sigs[i].operator String();
|
||||
if (cn.begins_with("L") && cn.ends_with(";")) {
|
||||
cn = cn.substr(1, cn.length() - 2);
|
||||
}
|
||||
jclass c = env->FindClass(cn.utf8().get_data());
|
||||
if (!c || !env->IsInstanceOf(jo->instance, c)) {
|
||||
arg_expected = Variant::OBJECT;
|
||||
|
|
@ -132,6 +135,116 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
|||
}
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::BOOL) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
||||
if (p_args[i]->get_type() != Variant::PACKED_BYTE_ARRAY) {
|
||||
arg_expected = Variant::PACKED_BYTE_ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_SHORT:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::PACKED_INT32_ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::PACKED_INT64_ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT32_ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT64_ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::STRING) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else if (p_args[i]->get_type() != Variant::PACKED_STRING_ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::CALLABLE) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} else {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
if (arr.is_typed() && arr.get_typed_builtin() != Variant::OBJECT) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
} else {
|
||||
String cn = E.param_sigs[i].operator String();
|
||||
if (cn.begins_with("[L") && cn.ends_with(";")) {
|
||||
cn = cn.substr(2, cn.length() - 3);
|
||||
}
|
||||
jclass c = env->FindClass(cn.utf8().get_data());
|
||||
if (c) {
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
Ref<JavaObject> jo = arr[j];
|
||||
if (jo.is_valid()) {
|
||||
if (!env->IsInstanceOf(jo->instance, c)) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
arg_expected = Variant::ARRAY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arg_expected = Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
if (p_args[i]->get_type() != Variant::ARRAY) {
|
||||
arg_expected = Variant::ARRAY;
|
||||
|
|
@ -287,13 +400,16 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
|||
to_free.push_back(jcallable);
|
||||
} break;
|
||||
case ARG_TYPE_CLASS: {
|
||||
Ref<JavaObject> jo = *p_args[i];
|
||||
if (jo.is_valid()) {
|
||||
argv[i].l = jo->instance;
|
||||
if (p_args[i]->get_type() == Variant::DICTIONARY) {
|
||||
argv[i].l = _variant_to_jvalue(env, Variant::DICTIONARY, p_args[i]).obj;
|
||||
} else {
|
||||
argv[i].l = nullptr; //I hope this works
|
||||
Ref<JavaObject> jo = *p_args[i];
|
||||
if (jo.is_valid()) {
|
||||
argv[i].l = jo->instance;
|
||||
} else {
|
||||
argv[i].l = nullptr; //I hope this works
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
||||
Array arr = *p_args[i];
|
||||
|
|
@ -307,90 +423,171 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
|||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
||||
Array arr = *p_args[i];
|
||||
jbyteArray a = env->NewByteArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jbyte val = arr[j];
|
||||
env->SetByteArrayRegion(a, j, 1, &val);
|
||||
jbyteArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewByteArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jbyte val = arr[j];
|
||||
env->SetByteArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
|
||||
PackedByteArray arr = *p_args[i];
|
||||
a = env->NewByteArray(arr.size());
|
||||
env->SetByteArrayRegion(a, 0, arr.size(), (const jbyte *)arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
||||
Array arr = *p_args[i];
|
||||
jcharArray a = env->NewCharArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jchar val = arr[j];
|
||||
env->SetCharArrayRegion(a, j, 1, &val);
|
||||
jcharArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewCharArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jchar val = arr[j];
|
||||
env->SetCharArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
|
||||
PackedByteArray arr = *p_args[i];
|
||||
// The data is expected to be UTF-16 encoded, so the length is half the size of the byte array.
|
||||
int size = arr.size() / 2;
|
||||
a = env->NewCharArray(size);
|
||||
env->SetCharArrayRegion(a, 0, size, (const jchar *)arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
||||
Array arr = *p_args[i];
|
||||
jshortArray a = env->NewShortArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jshort val = arr[j];
|
||||
env->SetShortArrayRegion(a, j, 1, &val);
|
||||
jshortArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewShortArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jshort val = arr[j];
|
||||
env->SetShortArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
|
||||
PackedInt32Array arr = *p_args[i];
|
||||
a = env->NewShortArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jshort val = arr[j];
|
||||
env->SetShortArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
||||
Array arr = *p_args[i];
|
||||
jintArray a = env->NewIntArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jint val = arr[j];
|
||||
env->SetIntArrayRegion(a, j, 1, &val);
|
||||
jintArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewIntArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jint val = arr[j];
|
||||
env->SetIntArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
|
||||
PackedInt32Array arr = *p_args[i];
|
||||
a = env->NewIntArray(arr.size());
|
||||
env->SetIntArrayRegion(a, 0, arr.size(), arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
||||
Array arr = *p_args[i];
|
||||
jlongArray a = env->NewLongArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jlong val = (int64_t)arr[j];
|
||||
env->SetLongArrayRegion(a, j, 1, &val);
|
||||
jlongArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewLongArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jlong val = (int64_t)arr[j];
|
||||
env->SetLongArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_INT64_ARRAY) {
|
||||
PackedInt64Array arr = *p_args[i];
|
||||
a = env->NewLongArray(arr.size());
|
||||
env->SetLongArrayRegion(a, 0, arr.size(), arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
||||
Array arr = *p_args[i];
|
||||
jfloatArray a = env->NewFloatArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jfloat val = arr[j];
|
||||
env->SetFloatArrayRegion(a, j, 1, &val);
|
||||
jfloatArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewFloatArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jfloat val = arr[j];
|
||||
env->SetFloatArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT32_ARRAY) {
|
||||
PackedFloat32Array arr = *p_args[i];
|
||||
a = env->NewFloatArray(arr.size());
|
||||
env->SetFloatArrayRegion(a, 0, arr.size(), arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
||||
Array arr = *p_args[i];
|
||||
jdoubleArray a = env->NewDoubleArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jdouble val = arr[j];
|
||||
env->SetDoubleArrayRegion(a, j, 1, &val);
|
||||
jdoubleArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewDoubleArray(arr.size());
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
jdouble val = arr[j];
|
||||
env->SetDoubleArrayRegion(a, j, 1, &val);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT64_ARRAY) {
|
||||
PackedFloat64Array arr = *p_args[i];
|
||||
a = env->NewDoubleArray(arr.size());
|
||||
env->SetDoubleArrayRegion(a, 0, arr.size(), arr.ptr());
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
to_free.push_back(a);
|
||||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
||||
Array arr = *p_args[i];
|
||||
jobjectArray a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
String s = arr[j];
|
||||
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
||||
env->SetObjectArrayElement(a, j, jStr);
|
||||
to_free.push_back(jStr);
|
||||
jobjectArray a = nullptr;
|
||||
|
||||
if (p_args[i]->get_type() == Variant::ARRAY) {
|
||||
Array arr = *p_args[i];
|
||||
a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
String s = arr[j];
|
||||
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
||||
env->SetObjectArrayElement(a, j, jStr);
|
||||
to_free.push_back(jStr);
|
||||
}
|
||||
} else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) {
|
||||
PackedStringArray arr = *p_args[i];
|
||||
a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
String s = arr[j];
|
||||
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
||||
env->SetObjectArrayElement(a, j, jStr);
|
||||
to_free.push_back(jStr);
|
||||
}
|
||||
}
|
||||
|
||||
argv[i].l = a;
|
||||
|
|
@ -410,7 +607,22 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
|||
to_free.push_back(jarr);
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
||||
argv[i].l = nullptr;
|
||||
String cn = method->param_sigs[i].operator String();
|
||||
if (cn.begins_with("[L") && cn.ends_with(";")) {
|
||||
cn = cn.substr(2, cn.length() - 3);
|
||||
}
|
||||
jclass c = env->FindClass(cn.utf8().get_data());
|
||||
if (c) {
|
||||
Array arr = *p_args[i];
|
||||
jobjectArray jarr = env->NewObjectArray(arr.size(), c, nullptr);
|
||||
for (int j = 0; j < arr.size(); j++) {
|
||||
Ref<JavaObject> jo = arr[j];
|
||||
env->SetObjectArrayElement(jarr, j, jo->instance);
|
||||
}
|
||||
|
||||
argv[i].l = jarr;
|
||||
to_free.push_back(jarr);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -718,7 +930,7 @@ bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, St
|
|||
if (str_type.begins_with("[")) {
|
||||
t = JavaClass::ARG_ARRAY_BIT;
|
||||
strsig = "[";
|
||||
str_type = str_type.substr(1, str_type.length() - 1);
|
||||
str_type = str_type.substr(1);
|
||||
if (str_type.begins_with("[")) {
|
||||
print_line("Nested arrays not supported for type: " + str_type);
|
||||
return false;
|
||||
|
|
@ -790,7 +1002,7 @@ bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, St
|
|||
strsig += "Ljava/lang/Double;";
|
||||
} else {
|
||||
//a class likely
|
||||
strsig += "L" + str_type.replace(".", "/") + ";";
|
||||
strsig += "L" + str_type.replace_char('.', '/') + ";";
|
||||
t |= JavaClass::ARG_TYPE_CLASS;
|
||||
}
|
||||
|
||||
|
|
@ -865,8 +1077,13 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
env->DeleteLocalRef(java_class);
|
||||
|
||||
if (java_class_wrapped.is_valid()) {
|
||||
Ref<JavaObject> ret = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
|
||||
var = ret;
|
||||
String cn = java_class_wrapped->get_java_class_name();
|
||||
if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") {
|
||||
var = _jobject_to_variant(env, obj);
|
||||
} else {
|
||||
Ref<JavaObject> ret = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
|
||||
var = ret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -881,11 +1098,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jboolean val;
|
||||
env->GetBooleanArrayRegion((jbooleanArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
env->GetBooleanArrayRegion((jbooleanArray)arr, i, 1, &val);
|
||||
ret[i] = (bool)val;
|
||||
}
|
||||
|
||||
var = ret;
|
||||
|
|
@ -893,106 +1111,85 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
||||
Array ret;
|
||||
PackedByteArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jbyte val;
|
||||
env->GetByteArrayRegion((jbyteArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
}
|
||||
ret.resize(count);
|
||||
env->GetByteArrayRegion((jbyteArray)arr, 0, count, (int8_t *)ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
||||
Array ret;
|
||||
PackedByteArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jchar val;
|
||||
env->GetCharArrayRegion((jcharArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
}
|
||||
// Char arrays are UTF-16 encoded, so it's double the length.
|
||||
ret.resize(count * 2);
|
||||
env->GetCharArrayRegion((jcharArray)arr, 0, count, (jchar *)ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
||||
Array ret;
|
||||
PackedInt32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
int32_t *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jshort val;
|
||||
env->GetShortArrayRegion((jshortArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
env->GetShortArrayRegion((jshortArray)arr, i, 1, &val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
||||
Array ret;
|
||||
PackedInt32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jint val;
|
||||
env->GetIntArrayRegion((jintArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
}
|
||||
ret.resize(count);
|
||||
env->GetIntArrayRegion((jintArray)arr, 0, count, ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
||||
Array ret;
|
||||
PackedInt64Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jlong val;
|
||||
env->GetLongArrayRegion((jlongArray)arr, 0, 1, &val);
|
||||
ret.push_back((int64_t)val);
|
||||
}
|
||||
ret.resize(count);
|
||||
env->GetLongArrayRegion((jlongArray)arr, 0, count, ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
||||
Array ret;
|
||||
PackedFloat32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jfloat val;
|
||||
env->GetFloatArrayRegion((jfloatArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
}
|
||||
ret.resize(count);
|
||||
env->GetFloatArrayRegion((jfloatArray)arr, 0, count, ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
||||
Array ret;
|
||||
PackedFloat64Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jdouble val;
|
||||
env->GetDoubleArrayRegion((jdoubleArray)arr, 0, 1, &val);
|
||||
ret.push_back(val);
|
||||
}
|
||||
ret.resize(count);
|
||||
env->GetDoubleArrayRegion((jdoubleArray)arr, 0, count, ret.ptrw());
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
|
|
@ -1002,14 +1199,13 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
} else {
|
||||
if (o) {
|
||||
bool val = env->CallBooleanMethod(o, JavaClassWrapper::singleton->Boolean_booleanValue);
|
||||
ret.push_back(val);
|
||||
ret[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1019,18 +1215,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
||||
Array ret;
|
||||
PackedByteArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
uint8_t *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0;
|
||||
} else {
|
||||
int val = env->CallByteMethod(o, JavaClassWrapper::singleton->Byte_byteValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = (uint8_t)val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1039,18 +1237,22 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
||||
Array ret;
|
||||
PackedByteArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
// Char arrays are UTF-16 encoded, so it's double the length.
|
||||
ret.resize(count * 2);
|
||||
|
||||
jchar *ptr = (jchar *)ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
count = i;
|
||||
break;
|
||||
} else {
|
||||
int val = env->CallCharMethod(o, JavaClassWrapper::singleton->Character_characterValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = (jchar)val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1059,18 +1261,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
||||
Array ret;
|
||||
PackedInt32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
int32_t *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0;
|
||||
} else {
|
||||
int val = env->CallShortMethod(o, JavaClassWrapper::singleton->Short_shortValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1079,18 +1283,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
||||
Array ret;
|
||||
PackedInt32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
int32_t *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0;
|
||||
} else {
|
||||
int val = env->CallIntMethod(o, JavaClassWrapper::singleton->Integer_integerValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1099,18 +1305,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
||||
Array ret;
|
||||
PackedInt64Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
int64_t *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0;
|
||||
} else {
|
||||
int64_t val = env->CallLongMethod(o, JavaClassWrapper::singleton->Long_longValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1119,18 +1327,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
||||
Array ret;
|
||||
PackedFloat32Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
float *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0.0;
|
||||
} else {
|
||||
float val = env->CallFloatMethod(o, JavaClassWrapper::singleton->Float_floatValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1139,18 +1349,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
||||
Array ret;
|
||||
PackedFloat64Array ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
double *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
ptr[i] = 0.0;
|
||||
} else {
|
||||
double val = env->CallDoubleMethod(o, JavaClassWrapper::singleton->Double_doubleValue);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1160,18 +1372,18 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
} break;
|
||||
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
|
||||
Array ret;
|
||||
PackedStringArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
String *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
} else {
|
||||
if (o) {
|
||||
String val = jstring_to_string((jstring)o, env);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1180,18 +1392,18 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
||||
Array ret;
|
||||
PackedStringArray ret;
|
||||
jobjectArray arr = (jobjectArray)obj;
|
||||
|
||||
int count = env->GetArrayLength(arr);
|
||||
ret.resize(count);
|
||||
|
||||
String *ptr = ret.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(arr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
} else {
|
||||
if (o) {
|
||||
String val = charsequence_to_string(env, o);
|
||||
ret.push_back(val);
|
||||
ptr[i] = val;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1203,13 +1415,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
Array ret;
|
||||
jobjectArray jarr = (jobjectArray)obj;
|
||||
int count = env->GetArrayLength(jarr);
|
||||
ret.resize(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject o = env->GetObjectArrayElement(jarr, i);
|
||||
if (!o) {
|
||||
ret.push_back(Variant());
|
||||
} else {
|
||||
if (o) {
|
||||
Callable callable = jcallable_to_callable(env, o);
|
||||
ret.push_back(callable);
|
||||
ret[i] = callable;
|
||||
}
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
|
|
@ -1218,6 +1429,32 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
return true;
|
||||
} break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
||||
Array ret;
|
||||
jobjectArray jarr = (jobjectArray)obj;
|
||||
int count = env->GetArrayLength(jarr);
|
||||
ret.resize(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
jobject obj = env->GetObjectArrayElement(jarr, i);
|
||||
if (obj) {
|
||||
jclass java_class = env->GetObjectClass(obj);
|
||||
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
|
||||
env->DeleteLocalRef(java_class);
|
||||
|
||||
if (java_class_wrapped.is_valid()) {
|
||||
String cn = java_class_wrapped->get_java_class_name();
|
||||
if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") {
|
||||
ret[i] = _jobject_to_variant(env, obj);
|
||||
} else {
|
||||
Ref<JavaObject> java_obj_wrapped = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
|
||||
ret[i] = java_obj_wrapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteLocalRef(obj);
|
||||
}
|
||||
|
||||
var = ret;
|
||||
return true;
|
||||
} break;
|
||||
}
|
||||
|
||||
|
|
@ -1225,7 +1462,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
|||
}
|
||||
|
||||
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_private_methods_access) {
|
||||
String class_name_dots = p_class.replace("/", ".");
|
||||
String class_name_dots = p_class.replace_char('/', '.');
|
||||
if (class_cache.has(class_name_dots)) {
|
||||
return class_cache[class_name_dots];
|
||||
}
|
||||
|
|
@ -1233,8 +1470,8 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
|||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, Ref<JavaClass>());
|
||||
|
||||
jclass bclass = env->FindClass(class_name_dots.replace(".", "/").utf8().get_data());
|
||||
ERR_FAIL_NULL_V(bclass, Ref<JavaClass>());
|
||||
jclass bclass = env->FindClass(class_name_dots.replace_char('.', '/').utf8().get_data());
|
||||
ERR_FAIL_NULL_V_MSG(bclass, Ref<JavaClass>(), vformat("Java class '%s' not found.", p_class));
|
||||
|
||||
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors);
|
||||
ERR_FAIL_NULL_V(constructors, Ref<JavaClass>());
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "java_godot_io_wrapper.h"
|
||||
|
||||
#include "core/error/error_list.h"
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JAVA_GODOT_IO_WRAPPER_H
|
||||
#define JAVA_GODOT_IO_WRAPPER_H
|
||||
#pragma once
|
||||
|
||||
#include "jni_utils.h"
|
||||
|
||||
|
|
@ -92,5 +91,3 @@ public:
|
|||
int get_screen_orientation();
|
||||
String get_system_dir(int p_dir, bool p_shared_storage);
|
||||
};
|
||||
|
||||
#endif // JAVA_GODOT_IO_WRAPPER_H
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "main/main.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
#ifndef XR_DISABLED
|
||||
#include "servers/xr_server.h"
|
||||
#endif // _3D_DISABLED
|
||||
#endif // XR_DISABLED
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_settings.h"
|
||||
|
|
@ -78,7 +79,6 @@ enum StartupStep {
|
|||
|
||||
static SafeNumeric<int> step; // Shared between UI and render threads
|
||||
|
||||
static Size2 new_size;
|
||||
static Vector3 accelerometer;
|
||||
static Vector3 gravity;
|
||||
static Vector3 magnetometer;
|
||||
|
|
@ -272,7 +272,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
|||
|
||||
if (step.get() == STEP_SHOW_LOGO) {
|
||||
bool xr_enabled = false;
|
||||
#ifndef _3D_DISABLED
|
||||
#ifndef XR_DISABLED
|
||||
// Unlike PCVR, there's no additional 2D screen onto which to render the boot logo,
|
||||
// so we skip this step if xr is enabled.
|
||||
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
|
||||
|
|
@ -280,7 +280,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
|||
} else {
|
||||
xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON;
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
#endif // XR_DISABLED
|
||||
if (!xr_enabled) {
|
||||
Main::setup_boot_logo();
|
||||
}
|
||||
|
|
@ -398,7 +398,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
|
|||
AndroidInputHandler::JoypadEvent jevent;
|
||||
jevent.device = p_device;
|
||||
jevent.type = AndroidInputHandler::JOY_EVENT_HAT;
|
||||
BitField<HatMask> hat;
|
||||
BitField<HatMask> hat = HatMask::CENTER;
|
||||
if (p_hat_x != 0) {
|
||||
if (p_hat_x < 0) {
|
||||
hat.set_flag(HatMask::LEFT);
|
||||
|
|
@ -548,6 +548,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN
|
|||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected) {
|
||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
ds->emit_hardware_keyboard_connection_changed(p_connected);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) {
|
||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JAVA_GODOT_LIB_JNI_H
|
||||
#define JAVA_GODOT_LIB_JNI_H
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
|
@ -69,6 +68,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorProjectMetad
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||
|
|
@ -77,5 +77,3 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getProjectResource
|
|||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_isEditorHint(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_isProjectManagerHint(JNIEnv *env, jclass clazz);
|
||||
}
|
||||
|
||||
#endif // JAVA_GODOT_LIB_JNI_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JAVA_GODOT_VIEW_WRAPPER_H
|
||||
#define JAVA_GODOT_VIEW_WRAPPER_H
|
||||
#pragma once
|
||||
|
||||
#include "jni_utils.h"
|
||||
|
||||
|
|
@ -66,5 +65,3 @@ public:
|
|||
|
||||
~GodotJavaViewWrapper();
|
||||
};
|
||||
|
||||
#endif // JAVA_GODOT_VIEW_WRAPPER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JAVA_GODOT_WRAPPER_H
|
||||
#define JAVA_GODOT_WRAPPER_H
|
||||
#pragma once
|
||||
|
||||
#include "java_godot_view_wrapper.h"
|
||||
|
||||
|
|
@ -141,5 +140,3 @@ public:
|
|||
|
||||
void on_editor_workspace_selected(const String &p_workspace);
|
||||
};
|
||||
|
||||
#endif // JAVA_GODOT_WRAPPER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JNI_UTILS_H
|
||||
#define JNI_UTILS_H
|
||||
#pragma once
|
||||
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
|
|
@ -91,11 +90,9 @@ static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) {
|
|||
}
|
||||
const char *const source_utf8 = env->GetStringUTFChars(source, nullptr);
|
||||
if (source_utf8) {
|
||||
result.parse_utf8(source_utf8);
|
||||
result.append_utf8(source_utf8);
|
||||
env->ReleaseStringUTFChars(source, source_utf8);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // JNI_UTILS_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NET_SOCKET_ANDROID_H
|
||||
#define NET_SOCKET_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "drivers/unix/net_socket_unix.h"
|
||||
|
||||
|
|
@ -74,5 +73,3 @@ public:
|
|||
NetSocketAndroid() {}
|
||||
~NetSocketAndroid();
|
||||
};
|
||||
|
||||
#endif // NET_SOCKET_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -454,6 +454,9 @@ void OS_Android::_load_system_font_config() const {
|
|||
Ref<XMLParser> parser;
|
||||
parser.instantiate();
|
||||
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
String root = String(getenv("ANDROID_ROOT")).path_join("fonts");
|
||||
|
||||
Error err = parser->open(String(getenv("ANDROID_ROOT")).path_join("/etc/fonts.xml"));
|
||||
if (err == OK) {
|
||||
bool in_font_node = false;
|
||||
|
|
@ -530,20 +533,22 @@ void OS_Android::_load_system_font_config() const {
|
|||
if (in_font_node) {
|
||||
fi.filename = parser->get_node_data().strip_edges();
|
||||
fi.font_name = fn;
|
||||
if (!fb.is_empty() && fn.is_empty()) {
|
||||
fi.font_name = fb;
|
||||
fi.priority = 2;
|
||||
if (da->file_exists(root.path_join(fi.filename))) {
|
||||
if (!fb.is_empty() && fn.is_empty()) {
|
||||
fi.font_name = fb;
|
||||
fi.priority = 2;
|
||||
}
|
||||
if (fi.font_name.is_empty()) {
|
||||
fi.font_name = "sans-serif";
|
||||
fi.priority = 5;
|
||||
}
|
||||
if (fi.font_name.ends_with("-condensed")) {
|
||||
fi.stretch = 75;
|
||||
fi.font_name = fi.font_name.trim_suffix("-condensed");
|
||||
}
|
||||
fonts.push_back(fi);
|
||||
font_names.insert(fi.font_name);
|
||||
}
|
||||
if (fi.font_name.is_empty()) {
|
||||
fi.font_name = "sans-serif";
|
||||
fi.priority = 5;
|
||||
}
|
||||
if (fi.font_name.ends_with("-condensed")) {
|
||||
fi.stretch = 75;
|
||||
fi.font_name = fi.font_name.trim_suffix("-condensed");
|
||||
}
|
||||
fonts.push_back(fi);
|
||||
font_names.insert(fi.font_name);
|
||||
}
|
||||
}
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef OS_ANDROID_H
|
||||
#define OS_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "audio_driver_opensl.h"
|
||||
|
||||
|
|
@ -192,5 +191,3 @@ private:
|
|||
static void _on_main_screen_changed(const String &p_screen_name);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // OS_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -28,5 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
#include <malloc.h>
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PLATFORM_GL_H
|
||||
#define PLATFORM_GL_H
|
||||
#pragma once
|
||||
|
||||
#ifndef GLES_API_ENABLED
|
||||
#define GLES_API_ENABLED // Allow using GLES.
|
||||
|
|
@ -39,5 +38,3 @@
|
|||
#include <EGL/eglext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
|
||||
#endif // PLATFORM_GL_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GODOT_PLUGIN_JNI_H
|
||||
#define GODOT_PLUGIN_JNI_H
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
|
@ -42,5 +41,3 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
|
||||
}
|
||||
|
||||
#endif // GODOT_PLUGIN_JNI_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H
|
||||
#define RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
|
|
@ -55,5 +54,3 @@ public:
|
|||
};
|
||||
|
||||
#endif // VULKAN_ENABLED
|
||||
|
||||
#endif // RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef THREAD_JANDROID_H
|
||||
#define THREAD_JANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
|
@ -37,5 +36,3 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env);
|
|||
|
||||
void setup_android_thread();
|
||||
JNIEnv *get_jni_env();
|
||||
|
||||
#endif // THREAD_JANDROID_H
|
||||
|
|
|
|||
|
|
@ -49,30 +49,37 @@ jmethodID TTS_Android::_stop_speaking = nullptr;
|
|||
|
||||
HashMap<int, Char16String> TTS_Android::ids;
|
||||
|
||||
void TTS_Android::initialize_tts() {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
if (_init) {
|
||||
env->CallVoidMethod(tts, _init);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TTS_Android::setup(jobject p_tts) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
tts = env->NewGlobalRef(p_tts);
|
||||
|
||||
jclass c = env->GetObjectClass(tts);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_init = env->GetMethodID(cls, "init", "()V");
|
||||
_is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
|
||||
_is_paused = env->GetMethodID(cls, "isPaused", "()Z");
|
||||
_get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
|
||||
_speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
|
||||
_pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
|
||||
_resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
|
||||
_stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
|
||||
|
||||
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
|
||||
if (tts_enabled) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
tts = env->NewGlobalRef(p_tts);
|
||||
|
||||
jclass c = env->GetObjectClass(tts);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_init = env->GetMethodID(cls, "init", "()V");
|
||||
_is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
|
||||
_is_paused = env->GetMethodID(cls, "isPaused", "()Z");
|
||||
_get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
|
||||
_speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
|
||||
_pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
|
||||
_resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
|
||||
_stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
|
||||
|
||||
if (_init) {
|
||||
env->CallVoidMethod(tts, _init);
|
||||
initialized = true;
|
||||
}
|
||||
initialize_tts();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,12 +87,19 @@ void TTS_Android::terminate() {
|
|||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL(env);
|
||||
|
||||
env->DeleteGlobalRef(cls);
|
||||
env->DeleteGlobalRef(tts);
|
||||
if (cls) {
|
||||
env->DeleteGlobalRef(cls);
|
||||
}
|
||||
if (tts) {
|
||||
env->DeleteGlobalRef(tts);
|
||||
}
|
||||
}
|
||||
|
||||
void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
|
||||
ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
if (ids.has(p_id)) {
|
||||
int pos = 0;
|
||||
if ((DisplayServer::TTSUtteranceEvent)p_event == DisplayServer::TTS_UTTERANCE_BOUNDARY) {
|
||||
|
|
@ -106,7 +120,10 @@ void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
|
|||
}
|
||||
|
||||
bool TTS_Android::is_speaking() {
|
||||
ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
if (_is_speaking) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
|
|
@ -118,7 +135,10 @@ bool TTS_Android::is_speaking() {
|
|||
}
|
||||
|
||||
bool TTS_Android::is_paused() {
|
||||
ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
if (_is_paused) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
|
|
@ -130,7 +150,10 @@ bool TTS_Android::is_paused() {
|
|||
}
|
||||
|
||||
Array TTS_Android::get_voices() {
|
||||
ERR_FAIL_COND_V_MSG(!initialized, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, Array());
|
||||
Array list;
|
||||
if (_get_voices) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
|
@ -158,7 +181,10 @@ Array TTS_Android::get_voices() {
|
|||
}
|
||||
|
||||
void TTS_Android::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) {
|
||||
ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
if (p_interrupt) {
|
||||
stop();
|
||||
}
|
||||
|
|
@ -183,7 +209,10 @@ void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volum
|
|||
}
|
||||
|
||||
void TTS_Android::pause() {
|
||||
ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
if (_pause_speaking) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
|
|
@ -193,7 +222,10 @@ void TTS_Android::pause() {
|
|||
}
|
||||
|
||||
void TTS_Android::resume() {
|
||||
ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
if (_resume_speaking) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
|
|
@ -203,7 +235,10 @@ void TTS_Android::resume() {
|
|||
}
|
||||
|
||||
void TTS_Android::stop() {
|
||||
ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
|
||||
if (unlikely(!initialized)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
for (const KeyValue<int, Char16String> &E : ids) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TTS_ANDROID_H
|
||||
#define TTS_ANDROID_H
|
||||
#pragma once
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
|
@ -55,6 +54,8 @@ class TTS_Android {
|
|||
|
||||
static HashMap<int, Char16String> ids;
|
||||
|
||||
static void initialize_tts();
|
||||
|
||||
public:
|
||||
static void setup(jobject p_tts);
|
||||
static void terminate();
|
||||
|
|
@ -68,5 +69,3 @@ public:
|
|||
static void resume();
|
||||
static void stop();
|
||||
};
|
||||
|
||||
#endif // TTS_ANDROID_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CALLABLE_JNI_H
|
||||
#define CALLABLE_JNI_H
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
|
@ -39,5 +38,3 @@ JNIEXPORT jobject JNICALL Java_org_godotengine_godot_variant_Callable_nativeCall
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_variant_Callable_nativeCallObjectDeferred(JNIEnv *p_env, jclass p_clazz, jlong p_object_id, jstring p_method_name, jobjectArray p_parameters);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_variant_Callable_releaseNativePointer(JNIEnv *p_env, jclass clazz, jlong p_native_pointer);
|
||||
}
|
||||
|
||||
#endif // CALLABLE_JNI_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue