Path: blob/master/platform/android/java_godot_wrapper.cpp
20938 views
/**************************************************************************/1/* java_godot_wrapper.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "java_godot_wrapper.h"3132#include "jni_utils.h"3334// JNIEnv is only valid within the thread it belongs to, in a multi threading environment35// we can't cache it.36// For Godot we call most access methods from our thread and we thus get a valid JNIEnv37// from get_jni_env(). For one or two we expect to pass the environment3839// TODO we could probably create a base class for this...4041GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {42godot_instance = p_env->NewGlobalRef(p_godot_instance);4344// get info about our Godot class so we can get pointers and stuff...45godot_class = jni_find_class(p_env, "org/godotengine/godot/Godot");46if (godot_class) {47godot_class = (jclass)p_env->NewGlobalRef(godot_class);48} else {49// this is a pretty serious fail.. bail... pointers will stay 050return;51}5253// get some Godot method pointers...54_restart = p_env->GetMethodID(godot_class, "restart", "()V");55_finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z");56_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");57_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");58_is_dark_mode_supported = p_env->GetMethodID(godot_class, "isDarkModeSupported", "()Z");59_is_dark_mode = p_env->GetMethodID(godot_class, "isDarkMode", "()Z");60_get_accent_color = p_env->GetMethodID(godot_class, "getAccentColor", "()I");61_get_base_color = p_env->GetMethodID(godot_class, "getBaseColor", "()I");62_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");63_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");64_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");65_show_dialog = p_env->GetMethodID(godot_class, "showDialog", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");66_show_input_dialog = p_env->GetMethodID(godot_class, "showInputDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");67_show_file_picker = p_env->GetMethodID(godot_class, "showFilePicker", "(Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)V");68_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");69_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");70_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");71_get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;");72_init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V");73_vibrate = p_env->GetMethodID(godot_class, "vibrate", "(II)V");74_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");75_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");76_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");77_on_godot_terminating = p_env->GetMethodID(godot_class, "onGodotTerminating", "()V");78_create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I");79_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");80_begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V");81_end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V");82_dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V");83_get_gdextension_list_config_file = p_env->GetMethodID(godot_class, "getGDExtensionConfigFiles", "()[Ljava/lang/String;");84_check_internal_feature_support = p_env->GetMethodID(godot_class, "checkInternalFeatureSupport", "(Ljava/lang/String;)Z");85_sign_apk = p_env->GetMethodID(godot_class, "nativeSignApk", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I");86_verify_apk = p_env->GetMethodID(godot_class, "nativeVerifyApk", "(Ljava/lang/String;)I");87_enable_immersive_mode = p_env->GetMethodID(godot_class, "nativeEnableImmersiveMode", "(Z)V");88_is_in_immersive_mode = p_env->GetMethodID(godot_class, "isInImmersiveMode", "()Z");89_set_window_color = p_env->GetMethodID(godot_class, "setWindowColor", "(Ljava/lang/String;)V");90_on_editor_workspace_selected = p_env->GetMethodID(godot_class, "nativeOnEditorWorkspaceSelected", "(Ljava/lang/String;)V");91_get_activity = p_env->GetMethodID(godot_class, "getActivity", "()Landroid/app/Activity;");92_build_env_connect = p_env->GetMethodID(godot_class, "nativeBuildEnvConnect", "(Lorg/godotengine/godot/variant/Callable;)Z");93_build_env_disconnect = p_env->GetMethodID(godot_class, "nativeBuildEnvDisconnect", "()V");94_build_env_execute = p_env->GetMethodID(godot_class, "nativeBuildEnvExecute", "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/godotengine/godot/variant/Callable;Lorg/godotengine/godot/variant/Callable;)I");95_build_env_cancel = p_env->GetMethodID(godot_class, "nativeBuildEnvCancel", "(I)V");96_build_env_clean_project = p_env->GetMethodID(godot_class, "nativeBuildEnvCleanProject", "(Ljava/lang/String;Ljava/lang/String;Lorg/godotengine/godot/variant/Callable;)V");97}9899GodotJavaWrapper::~GodotJavaWrapper() {100if (godot_view) {101delete godot_view;102}103104JNIEnv *env = get_jni_env();105ERR_FAIL_NULL(env);106env->DeleteGlobalRef(godot_instance);107env->DeleteGlobalRef(godot_class);108}109110jobject GodotJavaWrapper::get_activity() {111if (_get_activity) {112JNIEnv *env = get_jni_env();113ERR_FAIL_NULL_V(env, nullptr);114jobject activity = env->CallObjectMethod(godot_instance, _get_activity);115return activity;116}117return nullptr;118}119120GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {121if (godot_view != nullptr) {122return godot_view;123}124if (_get_render_view) {125JNIEnv *env = get_jni_env();126ERR_FAIL_NULL_V(env, nullptr);127jobject godot_render_view = env->CallObjectMethod(godot_instance, _get_render_view);128if (!env->IsSameObject(godot_render_view, nullptr)) {129godot_view = new GodotJavaViewWrapper(godot_render_view);130}131}132return godot_view;133}134135void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {136if (_on_godot_setup_completed) {137if (p_env == nullptr) {138p_env = get_jni_env();139}140p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed);141}142}143144void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {145if (_on_godot_main_loop_started) {146if (p_env == nullptr) {147p_env = get_jni_env();148}149ERR_FAIL_NULL(p_env);150p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);151}152}153154void GodotJavaWrapper::on_godot_terminating(JNIEnv *p_env) {155if (_on_godot_terminating) {156if (p_env == nullptr) {157p_env = get_jni_env();158}159ERR_FAIL_NULL(p_env);160p_env->CallVoidMethod(godot_instance, _on_godot_terminating);161}162}163164void GodotJavaWrapper::restart(JNIEnv *p_env) {165if (_restart) {166if (p_env == nullptr) {167p_env = get_jni_env();168}169ERR_FAIL_NULL(p_env);170p_env->CallVoidMethod(godot_instance, _restart);171}172}173174bool GodotJavaWrapper::force_quit(JNIEnv *p_env, int p_instance_id) {175if (_finish) {176if (p_env == nullptr) {177p_env = get_jni_env();178}179ERR_FAIL_NULL_V(p_env, false);180return p_env->CallBooleanMethod(godot_instance, _finish, p_instance_id);181}182return false;183}184185void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {186if (_set_keep_screen_on) {187JNIEnv *env = get_jni_env();188ERR_FAIL_NULL(env);189env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);190}191}192193void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {194if (_alert) {195JNIEnv *env = get_jni_env();196ERR_FAIL_NULL(env);197jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());198jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());199env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);200env->DeleteLocalRef(jStrMessage);201env->DeleteLocalRef(jStrTitle);202}203}204205bool GodotJavaWrapper::is_dark_mode_supported() {206if (_is_dark_mode_supported) {207JNIEnv *env = get_jni_env();208ERR_FAIL_NULL_V(env, false);209return env->CallBooleanMethod(godot_instance, _is_dark_mode_supported);210} else {211return false;212}213}214215bool GodotJavaWrapper::is_dark_mode() {216if (_is_dark_mode) {217JNIEnv *env = get_jni_env();218ERR_FAIL_NULL_V(env, false);219return env->CallBooleanMethod(godot_instance, _is_dark_mode);220} else {221return false;222}223}224225// Convert ARGB to RGBA.226static Color _argb_to_rgba(int p_color) {227int alpha = (p_color >> 24) & 0xFF;228int red = (p_color >> 16) & 0xFF;229int green = (p_color >> 8) & 0xFF;230int blue = p_color & 0xFF;231return Color(red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f);232}233234Color GodotJavaWrapper::get_accent_color() {235if (_get_accent_color) {236JNIEnv *env = get_jni_env();237ERR_FAIL_NULL_V(env, Color(0, 0, 0, 0));238int accent_color = env->CallIntMethod(godot_instance, _get_accent_color);239return _argb_to_rgba(accent_color);240} else {241return Color(0, 0, 0, 0);242}243}244245Color GodotJavaWrapper::get_base_color() {246if (_get_base_color) {247JNIEnv *env = get_jni_env();248ERR_FAIL_NULL_V(env, Color(0, 0, 0, 0));249int base_color = env->CallIntMethod(godot_instance, _get_base_color);250return _argb_to_rgba(base_color);251} else {252return Color(0, 0, 0, 0);253}254}255256bool GodotJavaWrapper::has_get_clipboard() {257return _get_clipboard != nullptr;258}259260String GodotJavaWrapper::get_clipboard() {261String clipboard;262if (_get_clipboard) {263JNIEnv *env = get_jni_env();264ERR_FAIL_NULL_V(env, String());265jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);266clipboard = jstring_to_string(s, env);267env->DeleteLocalRef(s);268}269return clipboard;270}271272String GodotJavaWrapper::get_input_fallback_mapping() {273String input_fallback_mapping;274if (_get_input_fallback_mapping) {275JNIEnv *env = get_jni_env();276ERR_FAIL_NULL_V(env, String());277jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);278input_fallback_mapping = jstring_to_string(fallback_mapping, env);279env->DeleteLocalRef(fallback_mapping);280}281return input_fallback_mapping;282}283284bool GodotJavaWrapper::has_set_clipboard() {285return _set_clipboard != nullptr;286}287288void GodotJavaWrapper::set_clipboard(const String &p_text) {289if (_set_clipboard) {290JNIEnv *env = get_jni_env();291ERR_FAIL_NULL(env);292jstring jStr = env->NewStringUTF(p_text.utf8().get_data());293env->CallVoidMethod(godot_instance, _set_clipboard, jStr);294env->DeleteLocalRef(jStr);295}296}297298bool GodotJavaWrapper::has_has_clipboard() {299return _has_clipboard != nullptr;300}301302bool GodotJavaWrapper::has_clipboard() {303if (_has_clipboard) {304JNIEnv *env = get_jni_env();305ERR_FAIL_NULL_V(env, false);306return env->CallBooleanMethod(godot_instance, _has_clipboard);307} else {308return false;309}310}311312Error GodotJavaWrapper::show_dialog(const String &p_title, const String &p_description, const Vector<String> &p_buttons) {313if (_show_input_dialog) {314JNIEnv *env = get_jni_env();315ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);316jstring j_title = env->NewStringUTF(p_title.utf8().get_data());317jstring j_description = env->NewStringUTF(p_description.utf8().get_data());318jobjectArray j_buttons = env->NewObjectArray(p_buttons.size(), jni_find_class(env, "java/lang/String"), nullptr);319for (int i = 0; i < p_buttons.size(); ++i) {320jstring j_button = env->NewStringUTF(p_buttons[i].utf8().get_data());321env->SetObjectArrayElement(j_buttons, i, j_button);322env->DeleteLocalRef(j_button);323}324env->CallVoidMethod(godot_instance, _show_dialog, j_title, j_description, j_buttons);325env->DeleteLocalRef(j_title);326env->DeleteLocalRef(j_description);327env->DeleteLocalRef(j_buttons);328return OK;329} else {330return ERR_UNCONFIGURED;331}332}333334Error GodotJavaWrapper::show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text) {335if (_show_input_dialog) {336JNIEnv *env = get_jni_env();337ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);338jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());339jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());340jstring jStrExistingText = env->NewStringUTF(p_existing_text.utf8().get_data());341env->CallVoidMethod(godot_instance, _show_input_dialog, jStrTitle, jStrMessage, jStrExistingText);342env->DeleteLocalRef(jStrTitle);343env->DeleteLocalRef(jStrMessage);344env->DeleteLocalRef(jStrExistingText);345return OK;346} else {347return ERR_UNCONFIGURED;348}349}350351Error GodotJavaWrapper::show_file_picker(const String &p_current_directory, const String &p_filename, int p_mode, const Vector<String> &p_filters) {352if (_show_file_picker) {353JNIEnv *env = get_jni_env();354ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);355jstring j_current_directory = env->NewStringUTF(p_current_directory.utf8().get_data());356jstring j_filename = env->NewStringUTF(p_filename.utf8().get_data());357jint j_mode = p_mode;358Vector<String> filters;359for (const String &E : p_filters) {360filters.append_array(E.get_slicec(';', 0).split(",")); // Add extensions.361filters.append_array(E.get_slicec(';', 2).split(",")); // Add MIME types.362}363jobjectArray j_filters = env->NewObjectArray(filters.size(), jni_find_class(env, "java/lang/String"), nullptr);364for (int i = 0; i < filters.size(); ++i) {365jstring j_filter = env->NewStringUTF(filters[i].utf8().get_data());366env->SetObjectArrayElement(j_filters, i, j_filter);367env->DeleteLocalRef(j_filter);368}369env->CallVoidMethod(godot_instance, _show_file_picker, j_current_directory, j_filename, j_mode, j_filters);370env->DeleteLocalRef(j_current_directory);371env->DeleteLocalRef(j_filename);372env->DeleteLocalRef(j_filters);373return OK;374} else {375return ERR_UNCONFIGURED;376}377}378379bool GodotJavaWrapper::request_permission(const String &p_name) {380if (_request_permission) {381JNIEnv *env = get_jni_env();382ERR_FAIL_NULL_V(env, false);383jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());384bool result = env->CallBooleanMethod(godot_instance, _request_permission, jStrName);385env->DeleteLocalRef(jStrName);386return result;387} else {388return false;389}390}391392bool GodotJavaWrapper::request_permissions() {393if (_request_permissions) {394JNIEnv *env = get_jni_env();395ERR_FAIL_NULL_V(env, false);396return env->CallBooleanMethod(godot_instance, _request_permissions);397} else {398return false;399}400}401402Vector<String> GodotJavaWrapper::get_granted_permissions() const {403Vector<String> permissions_list;404if (_get_granted_permissions) {405JNIEnv *env = get_jni_env();406ERR_FAIL_NULL_V(env, permissions_list);407jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);408jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);409410jsize len = env->GetArrayLength(*arr);411for (int i = 0; i < len; i++) {412jstring jstr = (jstring)env->GetObjectArrayElement(*arr, i);413String str = jstring_to_string(jstr, env);414permissions_list.push_back(str);415env->DeleteLocalRef(jstr);416}417}418return permissions_list;419}420421Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const {422Vector<String> config_file_list;423if (_get_gdextension_list_config_file) {424JNIEnv *env = get_jni_env();425ERR_FAIL_NULL_V(env, config_file_list);426jobject config_file_list_object = env->CallObjectMethod(godot_instance, _get_gdextension_list_config_file);427jobjectArray *arr = reinterpret_cast<jobjectArray *>(&config_file_list_object);428429jsize len = env->GetArrayLength(*arr);430for (int i = 0; i < len; i++) {431jstring j_config_file = (jstring)env->GetObjectArrayElement(*arr, i);432String config_file = jstring_to_string(j_config_file, env);433config_file_list.push_back(config_file);434env->DeleteLocalRef(j_config_file);435}436}437return config_file_list;438}439440String GodotJavaWrapper::get_ca_certificates() const {441String ca_certificates;442if (_get_ca_certificates) {443JNIEnv *env = get_jni_env();444ERR_FAIL_NULL_V(env, String());445jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates);446ca_certificates = jstring_to_string(s, env);447env->DeleteLocalRef(s);448}449return ca_certificates;450}451452void GodotJavaWrapper::init_input_devices() {453if (_init_input_devices) {454JNIEnv *env = get_jni_env();455ERR_FAIL_NULL(env);456env->CallVoidMethod(godot_instance, _init_input_devices);457}458}459460void GodotJavaWrapper::vibrate(int p_duration_ms, float p_amplitude) {461if (_vibrate) {462JNIEnv *env = get_jni_env();463ERR_FAIL_NULL(env);464465int j_amplitude = -1.0;466467if (p_amplitude != -1.0) {468j_amplitude = CLAMP(int(p_amplitude * 255), 1, 255);469}470471env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms, j_amplitude);472}473}474475int GodotJavaWrapper::create_new_godot_instance(const List<String> &args) {476if (_create_new_godot_instance) {477JNIEnv *env = get_jni_env();478ERR_FAIL_NULL_V(env, 0);479jobjectArray jargs = env->NewObjectArray(args.size(), jni_find_class(env, "java/lang/String"), env->NewStringUTF(""));480int i = 0;481for (List<String>::ConstIterator itr = args.begin(); itr != args.end(); ++itr, ++i) {482jstring j_arg = env->NewStringUTF(itr->utf8().get_data());483env->SetObjectArrayElement(jargs, i, j_arg);484env->DeleteLocalRef(j_arg);485}486return env->CallIntMethod(godot_instance, _create_new_godot_instance, jargs);487} else {488return 0;489}490}491492void GodotJavaWrapper::begin_benchmark_measure(const String &p_context, const String &p_label) {493if (_begin_benchmark_measure) {494JNIEnv *env = get_jni_env();495ERR_FAIL_NULL(env);496jstring j_context = env->NewStringUTF(p_context.utf8().get_data());497jstring j_label = env->NewStringUTF(p_label.utf8().get_data());498env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_context, j_label);499env->DeleteLocalRef(j_context);500env->DeleteLocalRef(j_label);501}502}503504void GodotJavaWrapper::end_benchmark_measure(const String &p_context, const String &p_label) {505if (_end_benchmark_measure) {506JNIEnv *env = get_jni_env();507ERR_FAIL_NULL(env);508jstring j_context = env->NewStringUTF(p_context.utf8().get_data());509jstring j_label = env->NewStringUTF(p_label.utf8().get_data());510env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_context, j_label);511env->DeleteLocalRef(j_context);512env->DeleteLocalRef(j_label);513}514}515516void GodotJavaWrapper::dump_benchmark(const String &benchmark_file) {517if (_dump_benchmark) {518JNIEnv *env = get_jni_env();519ERR_FAIL_NULL(env);520jstring j_benchmark_file = env->NewStringUTF(benchmark_file.utf8().get_data());521env->CallVoidMethod(godot_instance, _dump_benchmark, j_benchmark_file);522env->DeleteLocalRef(j_benchmark_file);523}524}525526bool GodotJavaWrapper::check_internal_feature_support(const String &p_feature) const {527if (_check_internal_feature_support) {528JNIEnv *env = get_jni_env();529ERR_FAIL_NULL_V(env, false);530531jstring j_feature = env->NewStringUTF(p_feature.utf8().get_data());532bool result = env->CallBooleanMethod(godot_instance, _check_internal_feature_support, j_feature);533env->DeleteLocalRef(j_feature);534return result;535} else {536return false;537}538}539540Error GodotJavaWrapper::sign_apk(const String &p_input_path, const String &p_output_path, const String &p_keystore_path, const String &p_keystore_user, const String &p_keystore_password) {541if (_sign_apk) {542JNIEnv *env = get_jni_env();543ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);544545jstring j_input_path = env->NewStringUTF(p_input_path.utf8().get_data());546jstring j_output_path = env->NewStringUTF(p_output_path.utf8().get_data());547jstring j_keystore_path = env->NewStringUTF(p_keystore_path.utf8().get_data());548jstring j_keystore_user = env->NewStringUTF(p_keystore_user.utf8().get_data());549jstring j_keystore_password = env->NewStringUTF(p_keystore_password.utf8().get_data());550551int result = env->CallIntMethod(godot_instance, _sign_apk, j_input_path, j_output_path, j_keystore_path, j_keystore_user, j_keystore_password);552553env->DeleteLocalRef(j_input_path);554env->DeleteLocalRef(j_output_path);555env->DeleteLocalRef(j_keystore_path);556env->DeleteLocalRef(j_keystore_user);557env->DeleteLocalRef(j_keystore_password);558559return static_cast<Error>(result);560} else {561return ERR_UNCONFIGURED;562}563}564565Error GodotJavaWrapper::verify_apk(const String &p_apk_path) {566if (_verify_apk) {567JNIEnv *env = get_jni_env();568ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);569570jstring j_apk_path = env->NewStringUTF(p_apk_path.utf8().get_data());571int result = env->CallIntMethod(godot_instance, _verify_apk, j_apk_path);572env->DeleteLocalRef(j_apk_path);573return static_cast<Error>(result);574} else {575return ERR_UNCONFIGURED;576}577}578579void GodotJavaWrapper::enable_immersive_mode(bool p_enabled) {580if (_enable_immersive_mode) {581JNIEnv *env = get_jni_env();582ERR_FAIL_NULL(env);583env->CallVoidMethod(godot_instance, _enable_immersive_mode, p_enabled);584}585}586587bool GodotJavaWrapper::is_in_immersive_mode() {588if (_is_in_immersive_mode) {589JNIEnv *env = get_jni_env();590ERR_FAIL_NULL_V(env, false);591return env->CallBooleanMethod(godot_instance, _is_in_immersive_mode);592} else {593return false;594}595}596597void GodotJavaWrapper::set_window_color(const Color &p_color) {598if (_set_window_color) {599JNIEnv *env = get_jni_env();600ERR_FAIL_NULL(env);601String color = "#" + p_color.to_html(false);602jstring jStrColor = env->NewStringUTF(color.utf8().get_data());603env->CallVoidMethod(godot_instance, _set_window_color, jStrColor);604}605}606607void GodotJavaWrapper::on_editor_workspace_selected(const String &p_workspace) {608if (_on_editor_workspace_selected) {609JNIEnv *env = get_jni_env();610ERR_FAIL_NULL(env);611612jstring j_workspace = env->NewStringUTF(p_workspace.utf8().get_data());613env->CallVoidMethod(godot_instance, _on_editor_workspace_selected, j_workspace);614}615}616617bool GodotJavaWrapper::build_env_connect(const Callable &p_callback) {618if (_build_env_connect) {619JNIEnv *env = get_jni_env();620ERR_FAIL_NULL_V(env, false);621622jobject j_callback = callable_to_jcallable(env, p_callback);623jboolean result = env->CallBooleanMethod(godot_instance, _build_env_connect, j_callback);624env->DeleteLocalRef(j_callback);625626return result;627}628629return false;630}631632void GodotJavaWrapper::build_env_disconnect() {633if (_build_env_disconnect) {634JNIEnv *env = get_jni_env();635ERR_FAIL_NULL(env);636637env->CallVoidMethod(godot_instance, _build_env_disconnect);638}639}640641int GodotJavaWrapper::build_env_execute(const String &p_build_tool, const List<String> &p_arguments, const String &p_project_path, const String &p_gradle_build_directory, const Callable &p_output_callback, const Callable &p_result_callback) {642if (_build_env_execute) {643JNIEnv *env = get_jni_env();644ERR_FAIL_NULL_V(env, -1);645646jstring j_build_tool = env->NewStringUTF(p_build_tool.utf8().get_data());647jobjectArray j_args = env->NewObjectArray(p_arguments.size(), env->FindClass("java/lang/String"), nullptr);648for (int i = 0; i < p_arguments.size(); i++) {649jstring j_arg = env->NewStringUTF(p_arguments.get(i).utf8().get_data());650env->SetObjectArrayElement(j_args, i, j_arg);651env->DeleteLocalRef(j_arg);652}653jstring j_project_path = env->NewStringUTF(p_project_path.utf8().get_data());654jstring j_gradle_build_directory = env->NewStringUTF(p_gradle_build_directory.utf8().get_data());655jobject j_output_callback = callable_to_jcallable(env, p_output_callback);656jobject j_result_callback = callable_to_jcallable(env, p_result_callback);657658jint result = env->CallIntMethod(godot_instance, _build_env_execute, j_build_tool, j_args, j_project_path, j_gradle_build_directory, j_output_callback, j_result_callback);659660env->DeleteLocalRef(j_build_tool);661env->DeleteLocalRef(j_args);662env->DeleteLocalRef(j_project_path);663env->DeleteLocalRef(j_gradle_build_directory);664env->DeleteLocalRef(j_output_callback);665env->DeleteLocalRef(j_result_callback);666667return result;668}669670return -1;671}672673void GodotJavaWrapper::build_env_cancel(int p_job_id) {674if (_build_env_cancel) {675JNIEnv *env = get_jni_env();676ERR_FAIL_NULL(env);677env->CallVoidMethod(godot_instance, _build_env_cancel, p_job_id);678}679}680681void GodotJavaWrapper::build_env_clean_project(const String &p_project_path, const String &p_gradle_build_directory, const Callable &p_callback) {682if (_build_env_clean_project) {683JNIEnv *env = get_jni_env();684ERR_FAIL_NULL(env);685686jstring j_project_path = env->NewStringUTF(p_project_path.utf8().get_data());687jstring j_gradle_build_directory = env->NewStringUTF(p_gradle_build_directory.utf8().get_data());688jobject j_callback = callable_to_jcallable(env, p_callback);689690env->CallVoidMethod(godot_instance, _build_env_clean_project, j_project_path, j_gradle_build_directory, j_callback);691692env->DeleteLocalRef(j_project_path);693env->DeleteLocalRef(j_gradle_build_directory);694env->DeleteLocalRef(j_callback);695}696}697698699