diff options
Diffstat (limited to 'platform/android')
96 files changed, 2512 insertions, 1665 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index f39eb8b889..ec42bc42b5 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -17,8 +17,8 @@ android_files = [ "java_godot_io_wrapper.cpp", "jni_utils.cpp", "android_keys_utils.cpp", - "vulkan/vk_renderer_jni.cpp", - "plugin/godot_plugin_jni.cpp", + "display_server_android.cpp", + "vulkan/vulkan_context_android.cpp", ] env_android = env.Clone() diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp index 88874ba2c7..b5b4fb9a4b 100644 --- a/platform/android/android_keys_utils.cpp +++ b/platform/android/android_keys_utils.cpp @@ -32,9 +32,7 @@ unsigned int android_get_keysym(unsigned int p_code) { for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) { - if (_ak_to_keycode[i].keycode == p_code) { - return _ak_to_keycode[i].keysym; } } diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index f076688ac8..fb442f4c54 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -154,7 +154,6 @@ enum { }; struct _WinTranslatePair { - unsigned int keysym; unsigned int keycode; }; diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp index ef11b12971..1f140f7119 100644 --- a/platform/android/api/api.cpp +++ b/platform/android/api/api.cpp @@ -32,15 +32,19 @@ #include "core/engine.h" #include "java_class_wrapper.h" +#include "jni_singleton.h" #if !defined(ANDROID_ENABLED) static JavaClassWrapper *java_class_wrapper = nullptr; #endif void register_android_api() { - #if !defined(ANDROID_ENABLED) + // On Android platforms, the `java_class_wrapper` instantiation and the + // `JNISingleton` registration occurs in + // `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup` java_class_wrapper = memnew(JavaClassWrapper); // Dummy + ClassDB::register_class<JNISingleton>(); #endif ClassDB::register_class<JavaClass>(); @@ -49,14 +53,12 @@ void register_android_api() { } void unregister_android_api() { - #if !defined(ANDROID_ENABLED) memdelete(java_class_wrapper); #endif } void JavaClassWrapper::_bind_methods() { - ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap); } diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h index 59fcd94b4d..e34f2a9f69 100644 --- a/platform/android/api/java_class_wrapper.h +++ b/platform/android/api/java_class_wrapper.h @@ -43,7 +43,6 @@ class JavaObject; #endif class JavaClass : public Reference { - GDCLASS(JavaClass, Reference); #ifdef ANDROID_ENABLED @@ -68,7 +67,6 @@ class JavaClass : public Reference { Map<StringName, Variant> constant_map; struct MethodInfo { - bool _static; Vector<uint32_t> param_types; Vector<StringName> param_sigs; @@ -77,15 +75,17 @@ class JavaClass : public Reference { }; _FORCE_INLINE_ static void _convert_to_variant_type(int p_sig, Variant::Type &r_type, float &likelihood) { - likelihood = 1.0; r_type = Variant::NIL; switch (p_sig) { - - case ARG_TYPE_VOID: r_type = Variant::NIL; break; + case ARG_TYPE_VOID: + r_type = Variant::NIL; + break; case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT: - case ARG_TYPE_BOOLEAN: r_type = Variant::BOOL; break; + case ARG_TYPE_BOOLEAN: + r_type = Variant::BOOL; + break; case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT: case ARG_TYPE_BYTE: r_type = Variant::INT; @@ -121,10 +121,18 @@ class JavaClass : public Reference { r_type = Variant::FLOAT; likelihood = 0.5; break; - case ARG_TYPE_STRING: r_type = Variant::STRING; break; - case ARG_TYPE_CLASS: r_type = Variant::OBJECT; break; - case ARG_ARRAY_BIT | ARG_TYPE_VOID: r_type = Variant::NIL; break; - case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: r_type = Variant::ARRAY; break; + case ARG_TYPE_STRING: + r_type = Variant::STRING; + break; + case ARG_TYPE_CLASS: + r_type = Variant::OBJECT; + break; + case ARG_ARRAY_BIT | ARG_TYPE_VOID: + r_type = Variant::NIL; + break; + case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: + r_type = Variant::ARRAY; + break; case ARG_ARRAY_BIT | ARG_TYPE_BYTE: r_type = Variant::PACKED_BYTE_ARRAY; likelihood = 1.0; @@ -153,8 +161,12 @@ class JavaClass : public Reference { r_type = Variant::PACKED_FLOAT32_ARRAY; likelihood = 0.5; break; - case ARG_ARRAY_BIT | ARG_TYPE_STRING: r_type = Variant::PACKED_STRING_ARRAY; break; - case ARG_ARRAY_BIT | ARG_TYPE_CLASS: r_type = Variant::ARRAY; break; + case ARG_ARRAY_BIT | ARG_TYPE_STRING: + r_type = Variant::PACKED_STRING_ARRAY; + break; + case ARG_ARRAY_BIT | ARG_TYPE_CLASS: + r_type = Variant::ARRAY; + break; } } @@ -174,7 +186,6 @@ public: }; class JavaObject : public Reference { - GDCLASS(JavaObject, Reference); #ifdef ANDROID_ENABLED @@ -194,7 +205,6 @@ public: }; class JavaClassWrapper : public Object { - GDCLASS(JavaClassWrapper, Object); #ifdef ANDROID_ENABLED diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h new file mode 100644 index 0000000000..ed69f8d6e4 --- /dev/null +++ b/platform/android/api/jni_singleton.h @@ -0,0 +1,223 @@ +/*************************************************************************/ +/* jni_singleton.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JNI_SINGLETON_H +#define JNI_SINGLETON_H + +#include <core/engine.h> +#include <core/variant.h> +#ifdef ANDROID_ENABLED +#include <platform/android/jni_utils.h> +#endif + +class JNISingleton : public Object { + GDCLASS(JNISingleton, Object); + +#ifdef ANDROID_ENABLED + struct MethodData { + jmethodID method; + Variant::Type ret_type; + Vector<Variant::Type> argtypes; + }; + + jobject instance; + Map<StringName, MethodData> method_map; +#endif + +public: + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +#ifdef ANDROID_ENABLED + Map<StringName, MethodData>::Element *E = method_map.find(p_method); + + // Check the method we're looking for is in the JNISingleton map and that + // the arguments match. + bool call_error = !E || E->get().argtypes.size() != p_argcount; + if (!call_error) { + for (int i = 0; i < p_argcount; i++) { + if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { + call_error = true; + break; + } + } + } + + if (call_error) { + // The method is not in this map, defaulting to the regular instance calls. + return Object::call(p_method, p_args, p_argcount, r_error); + } + + ERR_FAIL_COND_V(!instance, Variant()); + + r_error.error = Callable::CallError::CALL_OK; + + jvalue *v = nullptr; + + if (p_argcount) { + v = (jvalue *)alloca(sizeof(jvalue) * p_argcount); + } + + JNIEnv *env = ThreadAndroid::get_env(); + + int res = env->PushLocalFrame(16); + + ERR_FAIL_COND_V(res != 0, Variant()); + + List<jobject> to_erase; + for (int i = 0; i < p_argcount; i++) { + jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); + v[i] = vr.val; + if (vr.obj) + to_erase.push_back(vr.obj); + } + + Variant ret; + + switch (E->get().ret_type) { + case Variant::NIL: { + env->CallVoidMethodA(instance, E->get().method, v); + } break; + case Variant::BOOL: { + ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE; + } break; + case Variant::INT: { + ret = env->CallIntMethodA(instance, E->get().method, v); + } break; + case Variant::FLOAT: { + ret = env->CallFloatMethodA(instance, E->get().method, v); + } break; + case Variant::STRING: { + jobject o = env->CallObjectMethodA(instance, E->get().method, v); + ret = jstring_to_string((jstring)o, env); + env->DeleteLocalRef(o); + } break; + case Variant::PACKED_STRING_ARRAY: { + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); + + ret = _jobject_to_variant(env, arr); + + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_INT32_ARRAY: { + jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + Vector<int> sarr; + sarr.resize(fCount); + + int *w = sarr.ptrw(); + env->GetIntArrayRegion(arr, 0, fCount, w); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + Vector<float> sarr; + sarr.resize(fCount); + + float *w = sarr.ptrw(); + env->GetFloatArrayRegion(arr, 0, fCount, w); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + +#ifndef _MSC_VER +#warning This is missing 64 bits arrays, I have no idea how to do it in JNI +#endif + case Variant::DICTIONARY: { + jobject obj = env->CallObjectMethodA(instance, E->get().method, v); + ret = _jobject_to_variant(env, obj); + env->DeleteLocalRef(obj); + + } break; + default: { + env->PopLocalFrame(nullptr); + ERR_FAIL_V(Variant()); + } break; + } + + while (to_erase.size()) { + env->DeleteLocalRef(to_erase.front()->get()); + to_erase.pop_front(); + } + + env->PopLocalFrame(nullptr); + + return ret; +#else // ANDROID_ENABLED + + // Defaulting to the regular instance calls. + return Object::call(p_method, p_args, p_argcount, r_error); +#endif + } + +#ifdef ANDROID_ENABLED + jobject get_instance() const { + return instance; + } + + void set_instance(jobject p_instance) { + instance = p_instance; + } + + void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) { + MethodData md; + md.method = p_method; + md.argtypes = p_args; + md.ret_type = p_ret_type; + method_map[p_name] = md; + } + + void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) { + if (p_args.size() == 0) + ADD_SIGNAL(MethodInfo(p_name)); + else if (p_args.size() == 1) + ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"))); + else if (p_args.size() == 2) + ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"))); + else if (p_args.size() == 3) + ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"))); + else if (p_args.size() == 4) + ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"))); + else if (p_args.size() == 5) + ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5"))); + } + +#endif + + JNISingleton() { +#ifdef ANDROID_ENABLED + instance = nullptr; +#endif + } +}; + +#endif // JNI_SINGLETON_H diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 802d85e7be..09c981b3fa 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -52,12 +52,10 @@ Mutex AudioDriverAndroid::mutex; int32_t *AudioDriverAndroid::audioBuffer32 = nullptr; const char *AudioDriverAndroid::get_name() const { - return "Android"; } Error AudioDriverAndroid::init() { - /* // TODO: pass in/return a (Java) device ID, also whether we're opening for input or output this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples); @@ -75,9 +73,9 @@ Error AudioDriverAndroid::init() { // __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); JNIEnv *env = ThreadAndroid::get_env(); - int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", 44100); + int mix_rate = GLOBAL_GET("audio/mix_rate"); - int latency = GLOBAL_DEF_RST("audio/output_latency", 25); + int latency = GLOBAL_GET("audio/output_latency"); unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000); print_verbose("Audio buffer size: " + itos(buffer_size)); @@ -100,7 +98,6 @@ void AudioDriverAndroid::start() { } void AudioDriverAndroid::setup(jobject p_io) { - JNIEnv *env = ThreadAndroid::get_env(); io = p_io; @@ -114,10 +111,8 @@ void AudioDriverAndroid::setup(jobject p_io) { } void AudioDriverAndroid::thread_func(JNIEnv *env) { - jclass cls = env->FindClass("org/godotengine/godot/Godot"); if (cls) { - cls = (jclass)env->NewGlobalRef(cls); } jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;"); @@ -128,24 +123,20 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) { _write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V"); while (!quit) { - int16_t *ptr = (int16_t *)audioBufferPinned; int fc = audioBufferFrames; if (!s_ad->active || mutex.try_lock() != OK) { - for (int i = 0; i < fc; i++) { ptr[i] = 0; } } else { - s_ad->audio_server_process(fc / 2, audioBuffer32); mutex.unlock(); for (int i = 0; i < fc; i++) { - ptr[i] = audioBuffer32[i] >> 16; } } @@ -155,27 +146,22 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) { } int AudioDriverAndroid::get_mix_rate() const { - return mix_rate; } AudioDriver::SpeakerMode AudioDriverAndroid::get_speaker_mode() const { - return SPEAKER_MODE_STEREO; } void AudioDriverAndroid::lock() { - mutex.lock(); } void AudioDriverAndroid::unlock() { - mutex.unlock(); } void AudioDriverAndroid::finish() { - JNIEnv *env = ThreadAndroid::get_env(); env->CallVoidMethod(io, _quit); @@ -189,13 +175,11 @@ void AudioDriverAndroid::finish() { } void AudioDriverAndroid::set_pause(bool p_pause) { - JNIEnv *env = ThreadAndroid::get_env(); env->CallVoidMethod(io, _pause, p_pause); } AudioDriverAndroid::AudioDriverAndroid() { - s_ad = this; active = false; } diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h index b1cc3f9aa0..953ade9311 100644 --- a/platform/android/audio_driver_jandroid.h +++ b/platform/android/audio_driver_jandroid.h @@ -36,7 +36,6 @@ #include "java_godot_lib_jni.h" class AudioDriverAndroid : public AudioDriver { - static Mutex mutex; static AudioDriverAndroid *s_ad; static jobject io; diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index e59850e016..740e9a3132 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -39,7 +39,6 @@ void AudioDriverOpenSL::_buffer_callback( SLAndroidSimpleBufferQueueItf queueItf) { - bool mix = true; if (pause) { @@ -51,7 +50,6 @@ void AudioDriverOpenSL::_buffer_callback( if (mix) { audio_server_process(buffer_size, mixdown_buffer); } else { - int32_t *src_buff = mixdown_buffer; for (unsigned int i = 0; i < buffer_size * 2; i++) { src_buff[i] = 0; @@ -67,7 +65,6 @@ void AudioDriverOpenSL::_buffer_callback( last_free = (last_free + 1) % BUFFER_COUNT; for (unsigned int i = 0; i < buffer_size * 2; i++) { - ptr[i] = src_buff[i] >> 16; } @@ -77,7 +74,6 @@ void AudioDriverOpenSL::_buffer_callback( void AudioDriverOpenSL::_buffer_callbacks( SLAndroidSimpleBufferQueueItf queueItf, void *pContext) { - AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext; ad->_buffer_callback(queueItf); @@ -86,12 +82,10 @@ void AudioDriverOpenSL::_buffer_callbacks( AudioDriverOpenSL *AudioDriverOpenSL::s_ad = nullptr; const char *AudioDriverOpenSL::get_name() const { - return "Android"; } Error AudioDriverOpenSL::init() { - SLresult res; SLEngineOption EngineOption[] = { { (SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE } @@ -106,7 +100,6 @@ Error AudioDriverOpenSL::init() { } void AudioDriverOpenSL::start() { - active = false; SLresult res; @@ -114,7 +107,6 @@ void AudioDriverOpenSL::start() { buffer_size = 1024; for (int i = 0; i < BUFFER_COUNT; i++) { - buffers[i] = memnew_arr(int16_t, buffer_size * 2); memset(buffers[i], 0, buffer_size * 4); } @@ -204,7 +196,6 @@ void AudioDriverOpenSL::start() { } void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf queueItf) { - for (int i = 0; i < rec_buffer.size(); i++) { int32_t sample = rec_buffer[i] << 16; input_buffer_write(sample); @@ -216,14 +207,12 @@ void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf qu } void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) { - AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext; ad->_record_buffer_callback(queueItf); } Error AudioDriverOpenSL::capture_init_device() { - SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, @@ -291,7 +280,6 @@ Error AudioDriverOpenSL::capture_init_device() { } Error AudioDriverOpenSL::capture_start() { - if (OS::get_singleton()->request_permission("RECORD_AUDIO")) { return capture_init_device(); } @@ -300,7 +288,6 @@ Error AudioDriverOpenSL::capture_start() { } Error AudioDriverOpenSL::capture_stop() { - SLuint32 state; SLresult res = (*recordItf)->GetRecordState(recordItf, &state); ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN); @@ -317,34 +304,28 @@ Error AudioDriverOpenSL::capture_stop() { } int AudioDriverOpenSL::get_mix_rate() const { - return 44100; // hardcoded for Android, as selected by SL_SAMPLINGRATE_44_1 } AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const { - return SPEAKER_MODE_STEREO; } void AudioDriverOpenSL::lock() { - if (active) mutex.lock(); } void AudioDriverOpenSL::unlock() { - if (active) mutex.unlock(); } void AudioDriverOpenSL::finish() { - (*sl)->Destroy(sl); } void AudioDriverOpenSL::set_pause(bool p_pause) { - pause = p_pause; if (active) { diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h index 569e2aa54b..9858a40822 100644 --- a/platform/android/audio_driver_opensl.h +++ b/platform/android/audio_driver_opensl.h @@ -38,7 +38,6 @@ #include <SLES/OpenSLES_Android.h> class AudioDriverOpenSL : public AudioDriver { - bool active; Mutex mutex; diff --git a/platform/android/detect.py b/platform/android/detect.py index ed0643e3b3..6da1e5f3d6 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -25,7 +25,7 @@ def get_opts(): return [ ("ANDROID_NDK_ROOT", "Path to the Android NDK", os.environ.get("ANDROID_NDK_ROOT", 0)), - ("ndk_platform", 'Target platform (android-<api>, e.g. "android-18")', "android-18"), + ("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"), EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")), BoolVariable("android_neon", "Enable NEON support (armv7 only)", True), ] @@ -102,7 +102,7 @@ def configure(env): neon_text = "" if env["android_arch"] == "armv7" and env["android_neon"]: neon_text = " (with NEON)" - print("Building for Android (" + env["android_arch"] + ")" + neon_text) + print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")" + neon_text) can_vectorize = True if env["android_arch"] == "x86": @@ -314,8 +314,8 @@ def configure(env): ) env.Prepend(CPPPATH=["#platform/android"]) - env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"]) - env.Append(LIBS=["OpenSLES", "EGL", "GLESv3", "GLESv2", "android", "log", "z", "dl"]) + env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "VULKAN_ENABLED", "NO_FCNTL"]) + env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "vulkan", "android", "log", "z", "dl"]) # Return NDK version string in source.properties (adapted from the Chromium project). diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index f8571e6277..ca312b427f 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -42,12 +42,10 @@ jmethodID DirAccessJAndroid::_dir_close = nullptr; jmethodID DirAccessJAndroid::_dir_is_dir = nullptr; DirAccess *DirAccessJAndroid::create_fs() { - return memnew(DirAccessJAndroid); } Error DirAccessJAndroid::list_dir_begin() { - list_dir_end(); JNIEnv *env = ThreadAndroid::get_env(); @@ -62,7 +60,6 @@ Error DirAccessJAndroid::list_dir_begin() { } String DirAccessJAndroid::get_next() { - ERR_FAIL_COND_V(id == 0, ""); JNIEnv *env = ThreadAndroid::get_env(); @@ -76,19 +73,16 @@ String DirAccessJAndroid::get_next() { } bool DirAccessJAndroid::current_is_dir() const { - JNIEnv *env = ThreadAndroid::get_env(); return env->CallBooleanMethod(io, _dir_is_dir, id); } bool DirAccessJAndroid::current_is_hidden() const { - return current != "." && current != ".." && current.begins_with("."); } void DirAccessJAndroid::list_dir_end() { - if (id == 0) return; @@ -98,17 +92,14 @@ void DirAccessJAndroid::list_dir_end() { } int DirAccessJAndroid::get_drive_count() { - return 0; } String DirAccessJAndroid::get_drive(int p_drive) { - return ""; } Error DirAccessJAndroid::change_dir(String p_dir) { - JNIEnv *env = ThreadAndroid::get_env(); if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == "")) @@ -145,12 +136,10 @@ Error DirAccessJAndroid::change_dir(String p_dir) { } String DirAccessJAndroid::get_current_dir(bool p_include_drive) { - return "res://" + current_dir; } bool DirAccessJAndroid::file_exists(String p_file) { - String sd; if (current_dir == "") sd = p_file; @@ -165,7 +154,6 @@ bool DirAccessJAndroid::file_exists(String p_file) { } bool DirAccessJAndroid::dir_exists(String p_dir) { - JNIEnv *env = ThreadAndroid::get_env(); String sd; @@ -198,33 +186,27 @@ bool DirAccessJAndroid::dir_exists(String p_dir) { } Error DirAccessJAndroid::make_dir(String p_dir) { - ERR_FAIL_V(ERR_UNAVAILABLE); } Error DirAccessJAndroid::rename(String p_from, String p_to) { - ERR_FAIL_V(ERR_UNAVAILABLE); } Error DirAccessJAndroid::remove(String p_name) { - ERR_FAIL_V(ERR_UNAVAILABLE); } String DirAccessJAndroid::get_filesystem_type() const { - return "APK"; } //FileType get_file_type() const; size_t DirAccessJAndroid::get_space_left() { - return 0; } void DirAccessJAndroid::setup(jobject p_io) { - JNIEnv *env = ThreadAndroid::get_env(); io = p_io; @@ -240,11 +222,9 @@ void DirAccessJAndroid::setup(jobject p_io) { } DirAccessJAndroid::DirAccessJAndroid() { - id = 0; } DirAccessJAndroid::~DirAccessJAndroid() { - list_dir_end(); } diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index 8dab3e50ce..7d0def137a 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -36,7 +36,6 @@ #include <stdio.h> class DirAccessJAndroid : public DirAccess { - //AAssetDir* aad; static jobject io; diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp new file mode 100644 index 0000000000..48c23d3c31 --- /dev/null +++ b/platform/android/display_server_android.cpp @@ -0,0 +1,649 @@ +/*************************************************************************/ +/* display_server_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "display_server_android.h" + +#include "android_keys_utils.h" +#include "core/project_settings.h" +#include "java_godot_io_wrapper.h" +#include "java_godot_wrapper.h" +#include "os_android.h" + +#if defined(OPENGL_ENABLED) +#include "drivers/gles2/rasterizer_gles2.h" +#endif +#if defined(VULKAN_ENABLED) +#include "drivers/vulkan/rendering_device_vulkan.h" +#include "platform/android/vulkan/vulkan_context_android.h" +#include "servers/rendering/rasterizer_rd/rasterizer_rd.h" +#endif + +DisplayServerAndroid *DisplayServerAndroid::get_singleton() { + return (DisplayServerAndroid *)DisplayServer::get_singleton(); +} + +bool DisplayServerAndroid::has_feature(Feature p_feature) const { + switch (p_feature) { + //case FEATURE_CONSOLE_WINDOW: + //case FEATURE_CURSOR_SHAPE: + //case FEATURE_CUSTOM_CURSOR_SHAPE: + //case FEATURE_GLOBAL_MENU: + //case FEATURE_HIDPI: + //case FEATURE_ICON: + //case FEATURE_IME: + //case FEATURE_MOUSE: + //case FEATURE_MOUSE_WARP: + //case FEATURE_NATIVE_DIALOG: + //case FEATURE_NATIVE_ICON: + //case FEATURE_NATIVE_VIDEO: + //case FEATURE_WINDOW_TRANSPARENCY: + case FEATURE_CLIPBOARD: + case FEATURE_KEEP_SCREEN_ON: + case FEATURE_ORIENTATION: + case FEATURE_TOUCHSCREEN: + case FEATURE_VIRTUAL_KEYBOARD: + return true; + default: + return false; + } +} + +String DisplayServerAndroid::get_name() const { + return "Android"; +} + +void DisplayServerAndroid::clipboard_set(const String &p_text) { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_COND(!godot_java); + + if (godot_java->has_set_clipboard()) { + godot_java->set_clipboard(p_text); + } else { + DisplayServer::clipboard_set(p_text); + } +} + +String DisplayServerAndroid::clipboard_get() const { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_COND_V(!godot_java, String()); + + if (godot_java->has_get_clipboard()) { + return godot_java->get_clipboard(); + } else { + return DisplayServer::clipboard_get(); + } +} + +void DisplayServerAndroid::screen_set_keep_on(bool p_enable) { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_COND(!godot_java); + + godot_java->set_keep_screen_on(p_enable); + keep_screen_on = p_enable; +} + +bool DisplayServerAndroid::screen_is_kept_on() const { + return keep_screen_on; +} + +void DisplayServerAndroid::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND(!godot_io_java); + + godot_io_java->set_screen_orientation(p_orientation); +} + +DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(int p_screen) const { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND_V(!godot_io_java, SCREEN_LANDSCAPE); + + return (ScreenOrientation)godot_io_java->get_screen_orientation(); +} + +int DisplayServerAndroid::get_screen_count() const { + return 1; +} + +Point2i DisplayServerAndroid::screen_get_position(int p_screen) const { + return Point2i(0, 0); +} + +Size2i DisplayServerAndroid::screen_get_size(int p_screen) const { + return OS_Android::get_singleton()->get_display_size(); +} + +Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const { + Size2i display_size = OS_Android::get_singleton()->get_display_size(); + return Rect2i(0, 0, display_size.width, display_size.height); +} + +int DisplayServerAndroid::screen_get_dpi(int p_screen) const { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND_V(!godot_io_java, 0); + + return godot_io_java->get_screen_dpi(); +} + +bool DisplayServerAndroid::screen_is_touchscreen(int p_screen) const { + return true; +} + +void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_length) { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND(!godot_io_java); + + if (godot_io_java->has_vk()) { + godot_io_java->show_vk(p_existing_text, p_max_length); + } else { + ERR_PRINT("Virtual keyboard not available"); + } +} + +void DisplayServerAndroid::virtual_keyboard_hide() { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND(!godot_io_java); + + if (godot_io_java->has_vk()) { + godot_io_java->hide_vk(); + } else { + ERR_PRINT("Virtual keyboard not available"); + } +} + +int DisplayServerAndroid::virtual_keyboard_get_height() const { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_COND_V(!godot_io_java, 0); + + return godot_io_java->get_vk_height(); +} + +void DisplayServerAndroid::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { + window_event_callback = p_callable; +} + +void DisplayServerAndroid::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { + input_event_callback = p_callable; +} + +void DisplayServerAndroid::window_set_input_text_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { + input_text_callback = p_callable; +} + +void DisplayServerAndroid::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +void DisplayServerAndroid::_window_callback(const Callable &p_callable, const Variant &p_arg) const { + if (!p_callable.is_null()) { + const Variant *argp = &p_arg; + Variant ret; + Callable::CallError ce; + p_callable.call((const Variant **)&argp, 1, ret, ce); + } +} + +void DisplayServerAndroid::send_window_event(DisplayServer::WindowEvent p_event) const { + _window_callback(window_event_callback, int(p_event)); +} + +void DisplayServerAndroid::send_input_event(const Ref<InputEvent> &p_event) const { + _window_callback(input_event_callback, p_event); +} + +void DisplayServerAndroid::send_input_text(const String &p_text) const { + _window_callback(input_text_callback, p_text); +} + +void DisplayServerAndroid::_dispatch_input_events(const Ref<InputEvent> &p_event) { + DisplayServerAndroid::get_singleton()->send_input_event(p_event); +} + +Vector<DisplayServer::WindowID> DisplayServerAndroid::get_window_list() const { + Vector<WindowID> ret; + ret.push_back(MAIN_WINDOW_ID); + return ret; +} + +DisplayServer::WindowID DisplayServerAndroid::get_window_at_screen_position(const Point2i &p_position) const { + return MAIN_WINDOW_ID; +} + +void DisplayServerAndroid::window_attach_instance_id(ObjectID p_instance, DisplayServer::WindowID p_window) { + window_attached_instance_id = p_instance; +} + +ObjectID DisplayServerAndroid::window_get_attached_instance_id(DisplayServer::WindowID p_window) const { + return window_attached_instance_id; +} + +void DisplayServerAndroid::window_set_title(const String &p_title, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +int DisplayServerAndroid::window_get_current_screen(DisplayServer::WindowID p_window) const { + return SCREEN_OF_MAIN_WINDOW; +} + +void DisplayServerAndroid::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +Point2i DisplayServerAndroid::window_get_position(DisplayServer::WindowID p_window) const { + return Point2i(); +} + +void DisplayServerAndroid::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +void DisplayServerAndroid::window_set_transient(DisplayServer::WindowID p_window, DisplayServer::WindowID p_parent) { + // Not supported on Android. +} + +void DisplayServerAndroid::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +Size2i DisplayServerAndroid::window_get_max_size(DisplayServer::WindowID p_window) const { + return Size2i(); +} + +void DisplayServerAndroid::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +Size2i DisplayServerAndroid::window_get_min_size(DisplayServer::WindowID p_window) const { + return Size2i(); +} + +void DisplayServerAndroid::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +Size2i DisplayServerAndroid::window_get_size(DisplayServer::WindowID p_window) const { + return OS_Android::get_singleton()->get_display_size(); +} + +Size2i DisplayServerAndroid::window_get_real_size(DisplayServer::WindowID p_window) const { + return OS_Android::get_singleton()->get_display_size(); +} + +void DisplayServerAndroid::window_set_mode(DisplayServer::WindowMode p_mode, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +DisplayServer::WindowMode DisplayServerAndroid::window_get_mode(DisplayServer::WindowID p_window) const { + return WINDOW_MODE_FULLSCREEN; +} + +bool DisplayServerAndroid::window_is_maximize_allowed(DisplayServer::WindowID p_window) const { + return false; +} + +void DisplayServerAndroid::window_set_flag(DisplayServer::WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const { + return false; +} + +void DisplayServerAndroid::window_request_attention(DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +void DisplayServerAndroid::window_move_to_foreground(DisplayServer::WindowID p_window) { + // Not supported on Android. +} + +bool DisplayServerAndroid::window_can_draw(DisplayServer::WindowID p_window) const { + return true; +} + +bool DisplayServerAndroid::can_any_window_draw() const { + return true; +} + +void DisplayServerAndroid::alert(const String &p_alert, const String &p_title) { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_COND(!godot_java); + + godot_java->alert(p_alert, p_title); +} + +void DisplayServerAndroid::process_events() { + // Nothing to do +} + +Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { + Vector<String> drivers; + +#ifdef OPENGL_ENABLED + drivers.push_back("opengl"); +#endif +#ifdef VULKAN_ENABLED + drivers.push_back("vulkan"); +#endif + + return drivers; +} + +DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + return memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); +} + +void DisplayServerAndroid::register_android_driver() { + register_create_function("android", create_func, get_rendering_drivers_func); +} + +DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + rendering_driver = p_rendering_driver; + + // TODO: rendering_driver is broken, change when different drivers are supported again + rendering_driver = "vulkan"; + + keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on"); + +#if defined(OPENGL_ENABLED) + if (rendering_driver == "opengl") { + bool gl_initialization_error = false; + + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + gl_initialization_error = true; + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n" + "Please try updating your Android version.", + "Unable to initialize video driver"); + return; + } + } +#endif + +#if defined(VULKAN_ENABLED) + context_vulkan = nullptr; + rendering_device_vulkan = nullptr; + + if (rendering_driver == "vulkan") { + ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window(); + ERR_FAIL_COND(!native_window); + + context_vulkan = memnew(VulkanContextAndroid); + if (context_vulkan->initialize() != OK) { + memdelete(context_vulkan); + context_vulkan = nullptr; + ERR_FAIL_MSG("Failed to initialize Vulkan context"); + } + + Size2i display_size = OS_Android::get_singleton()->get_display_size(); + if (context_vulkan->window_create(native_window, display_size.width, display_size.height) == -1) { + memdelete(context_vulkan); + context_vulkan = nullptr; + ERR_FAIL_MSG("Failed to create Vulkan window."); + } + + rendering_device_vulkan = memnew(RenderingDeviceVulkan); + rendering_device_vulkan->initialize(context_vulkan); + + RasterizerRD::make_current(); + } +#endif + + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); +} + +DisplayServerAndroid::~DisplayServerAndroid() { +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + if (rendering_device_vulkan) { + rendering_device_vulkan->finalize(); + memdelete(rendering_device_vulkan); + } + + if (context_vulkan) { + memdelete(context_vulkan); + } + } +#endif +} + +void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p_event) { + switch (p_event.type) { + case JOY_EVENT_BUTTON: + Input::get_singleton()->joy_button(p_event.device, p_event.index, p_event.pressed); + break; + case JOY_EVENT_AXIS: + Input::JoyAxis value; + value.min = -1; + value.value = p_event.value; + Input::get_singleton()->joy_axis(p_event.device, p_event.index, value); + break; + case JOY_EVENT_HAT: + Input::get_singleton()->joy_hat(p_event.device, p_event.hat); + break; + default: + return; + } +} + +void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) { + Ref<InputEventKey> ev; + ev.instance(); + int val = p_unicode_char; + int keycode = android_get_keysym(p_keycode); + int phy_keycode = android_get_keysym(p_scancode); + ev->set_keycode(keycode); + ev->set_physical_keycode(phy_keycode); + ev->set_unicode(val); + ev->set_pressed(p_pressed); + + if (val == '\n') { + ev->set_keycode(KEY_ENTER); + } else if (val == 61448) { + ev->set_keycode(KEY_BACKSPACE); + ev->set_unicode(KEY_BACKSPACE); + } else if (val == 61453) { + ev->set_keycode(KEY_ENTER); + ev->set_unicode(KEY_ENTER); + } else if (p_keycode == 4) { + OS_Android::get_singleton()->main_loop_request_go_back(); + } + + Input::get_singleton()->parse_input_event(ev); +} + +void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector<DisplayServerAndroid::TouchPos> &p_points) { + switch (p_what) { + case 0: { //gesture begin + if (touch.size()) { + //end all if exist + for (int i = 0; i < touch.size(); i++) { + Ref<InputEventScreenTouch> ev; + ev.instance(); + ev->set_index(touch[i].id); + ev->set_pressed(false); + ev->set_position(touch[i].pos); + Input::get_singleton()->parse_input_event(ev); + } + } + + touch.resize(p_points.size()); + for (int i = 0; i < p_points.size(); i++) { + touch.write[i].id = p_points[i].id; + touch.write[i].pos = p_points[i].pos; + } + + //send touch + for (int i = 0; i < touch.size(); i++) { + Ref<InputEventScreenTouch> ev; + ev.instance(); + ev->set_index(touch[i].id); + ev->set_pressed(true); + ev->set_position(touch[i].pos); + Input::get_singleton()->parse_input_event(ev); + } + + } break; + case 1: { //motion + ERR_FAIL_COND(touch.size() != p_points.size()); + + for (int i = 0; i < touch.size(); i++) { + int idx = -1; + for (int j = 0; j < p_points.size(); j++) { + if (touch[i].id == p_points[j].id) { + idx = j; + break; + } + } + + ERR_CONTINUE(idx == -1); + + if (touch[i].pos == p_points[idx].pos) + continue; //no move unncesearily + + Ref<InputEventScreenDrag> ev; + ev.instance(); + ev->set_index(touch[i].id); + ev->set_position(p_points[idx].pos); + ev->set_relative(p_points[idx].pos - touch[i].pos); + Input::get_singleton()->parse_input_event(ev); + touch.write[i].pos = p_points[idx].pos; + } + + } break; + case 2: { //release + if (touch.size()) { + //end all if exist + for (int i = 0; i < touch.size(); i++) { + Ref<InputEventScreenTouch> ev; + ev.instance(); + ev->set_index(touch[i].id); + ev->set_pressed(false); + ev->set_position(touch[i].pos); + Input::get_singleton()->parse_input_event(ev); + } + touch.clear(); + } + } break; + case 3: { // add touch + for (int i = 0; i < p_points.size(); i++) { + if (p_points[i].id == p_pointer) { + TouchPos tp = p_points[i]; + touch.push_back(tp); + + Ref<InputEventScreenTouch> ev; + ev.instance(); + + ev->set_index(tp.id); + ev->set_pressed(true); + ev->set_position(tp.pos); + Input::get_singleton()->parse_input_event(ev); + + break; + } + } + } break; + case 4: { // remove touch + for (int i = 0; i < touch.size(); i++) { + if (touch[i].id == p_pointer) { + Ref<InputEventScreenTouch> ev; + ev.instance(); + ev->set_index(touch[i].id); + ev->set_pressed(false); + ev->set_position(touch[i].pos); + Input::get_singleton()->parse_input_event(ev); + touch.remove(i); + + break; + } + } + } break; + } +} + +void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) { + // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER + switch (p_type) { + case 7: // hover move + case 9: // hover enter + case 10: { // hover exit + Ref<InputEventMouseMotion> ev; + ev.instance(); + ev->set_position(p_pos); + ev->set_global_position(p_pos); + ev->set_relative(p_pos - hover_prev_pos); + Input::get_singleton()->parse_input_event(ev); + hover_prev_pos = p_pos; + } break; + } +} + +void DisplayServerAndroid::process_double_tap(Point2 p_pos) { + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_position(p_pos); + ev->set_global_position(p_pos); + ev->set_pressed(false); + ev->set_doubleclick(true); + Input::get_singleton()->parse_input_event(ev); +} + +void DisplayServerAndroid::process_scroll(Point2 p_pos) { + Ref<InputEventPanGesture> ev; + ev.instance(); + ev->set_position(p_pos); + ev->set_delta(p_pos - scroll_prev_pos); + Input::get_singleton()->parse_input_event(ev); + scroll_prev_pos = p_pos; +} + +void DisplayServerAndroid::process_accelerometer(const Vector3 &p_accelerometer) { + Input::get_singleton()->set_accelerometer(p_accelerometer); +} + +void DisplayServerAndroid::process_gravity(const Vector3 &p_gravity) { + Input::get_singleton()->set_gravity(p_gravity); +} + +void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) { + Input::get_singleton()->set_magnetometer(p_magnetometer); +} + +void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) { + Input::get_singleton()->set_gyroscope(p_gyroscope); +} diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h new file mode 100644 index 0000000000..b2909f258b --- /dev/null +++ b/platform/android/display_server_android.h @@ -0,0 +1,173 @@ +/*************************************************************************/ +/* display_server_android.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DISPLAY_SERVER_ANDROID_H +#define DISPLAY_SERVER_ANDROID_H + +#include "servers/display_server.h" + +#if defined(VULKAN_ENABLED) +class VulkanContextAndroid; +class RenderingDeviceVulkan; +#endif + +class DisplayServerAndroid : public DisplayServer { +public: + struct TouchPos { + int id; + Point2 pos; + }; + + enum { + JOY_EVENT_BUTTON = 0, + JOY_EVENT_AXIS = 1, + JOY_EVENT_HAT = 2 + }; + + struct JoypadEvent { + int device; + int type; + int index; + bool pressed; + float value; + int hat; + }; + +private: + String rendering_driver; + + bool keep_screen_on; + + Vector<TouchPos> touch; + Point2 hover_prev_pos; // needed to calculate the relative position on hover events + Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events + +#if defined(VULKAN_ENABLED) + VulkanContextAndroid *context_vulkan; + RenderingDeviceVulkan *rendering_device_vulkan; +#endif + + ObjectID window_attached_instance_id; + + Callable window_event_callback; + Callable input_event_callback; + Callable input_text_callback; + + void _window_callback(const Callable &p_callable, const Variant &p_arg) const; + + static void _dispatch_input_events(const Ref<InputEvent> &p_event); + +public: + static DisplayServerAndroid *get_singleton(); + + virtual bool has_feature(Feature p_feature) const; + virtual String get_name() const; + + virtual void clipboard_set(const String &p_text); + virtual String clipboard_get() const; + + virtual void screen_set_keep_on(bool p_enable); + virtual bool screen_is_kept_on() const; + + virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW); + virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + virtual int get_screen_count() const; + virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_length = -1); + virtual void virtual_keyboard_hide(); + virtual int virtual_keyboard_get_height() const; + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + + void send_window_event(WindowEvent p_event) const; + void send_input_event(const Ref<InputEvent> &p_event) const; + void send_input_text(const String &p_text) const; + + virtual Vector<WindowID> get_window_list() const; + virtual WindowID get_window_at_screen_position(const Point2i &p_position) const; + virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID); + virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID); + virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID); + virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_transient(WindowID p_window, WindowID p_parent); + virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID); + virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const; + virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID); + virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID); + virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID); + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const; + virtual bool can_any_window_draw() const; + + virtual void alert(const String &p_alert, const String &p_title); + + virtual void process_events(); + + void process_accelerometer(const Vector3 &p_accelerometer); + void process_gravity(const Vector3 &p_gravity); + void process_magnetometer(const Vector3 &p_magnetometer); + void process_gyroscope(const Vector3 &p_gyroscope); + void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); + void process_hover(int p_type, Point2 p_pos); + void process_double_tap(Point2 p_pos); + void process_scroll(Point2 p_pos); + void process_joy_event(JoypadEvent p_event); + void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed); + + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static Vector<String> get_rendering_drivers_func(); + static void register_android_driver(); + + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + ~DisplayServerAndroid(); +}; + +#endif // DISPLAY_SERVER_ANDROID_H diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b6c30d120c..dfaaf68b69 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -44,6 +44,7 @@ #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "platform/android/logo.gen.h" +#include "platform/android/plugin/godot_plugin_config.h" #include "platform/android/run_icon.gen.h" #include <string.h> @@ -235,14 +236,12 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun }; class EditorExportPlatformAndroid : public EditorExportPlatform { - GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); Ref<ImageTexture> logo; Ref<ImageTexture> run_icon; struct Device { - String id; String name; String description; @@ -250,26 +249,51 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { }; struct APKExportData { - zipFile apk; EditorProgress *ep; }; + Vector<PluginConfig> plugins; + volatile bool plugins_changed; + Mutex plugins_lock; Vector<Device> devices; volatile bool devices_changed; Mutex device_lock; - Thread *device_thread; + Thread *check_for_changes_thread; volatile bool quit_request; - static void _device_poll_thread(void *ud) { - + static void _check_for_changes_poll_thread(void *ud) { EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; while (!ea->quit_request) { + // Check for plugins updates + { + // Nothing to do if we already know the plugins have changed. + if (!ea->plugins_changed) { + Vector<PluginConfig> loaded_plugins = get_plugins(); + + MutexLock lock(ea->plugins_lock); + + if (ea->plugins.size() != loaded_plugins.size()) { + ea->plugins_changed = true; + } else { + for (int i = 0; i < ea->plugins.size(); i++) { + if (ea->plugins[i].name != loaded_plugins[i].name) { + ea->plugins_changed = true; + break; + } + } + } + if (ea->plugins_changed) { + ea->plugins = loaded_plugins; + } + } + } + + // Check for devices updates String adb = EditorSettings::get_singleton()->get("export/android/adb"); if (FileAccess::exists(adb)) { - String devices; List<String> args; args.push_back("devices"); @@ -279,11 +303,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { Vector<String> ds = devices.split("\n"); Vector<String> ldevices; for (int i = 1; i < ds.size(); i++) { - String d = ds[i]; int dpos = d.find("device"); - if (dpos == -1) + if (dpos == -1) { continue; + } d = d.substr(0, dpos).strip_edges(); ldevices.push_back(d); } @@ -293,12 +317,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { bool different = false; if (ea->devices.size() != ldevices.size()) { - different = true; } else { - for (int i = 0; i < ea->devices.size(); i++) { - if (ea->devices[i].id != ldevices[i]) { different = true; break; @@ -307,11 +328,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } if (different) { - Vector<Device> ndevices; for (int i = 0; i < ldevices.size(); i++) { - Device d; d.id = ldevices[i]; for (int j = 0; j < ea->devices.size(); j++) { @@ -340,7 +359,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { d.description = "Device ID: " + d.id + "\n"; d.api_level = 0; for (int j = 0; j < props.size(); j++) { - // got information by `shell cat /system/build.prop` before and its format is "property=value" // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above // its format is "[property]: [value]" so changed it as like build.prop @@ -372,7 +390,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } d.name = vendor + " " + device; - if (device == String()) continue; + if (device == String()) { + continue; + } } ndevices.push_back(d); @@ -388,8 +408,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { uint64_t time = OS::get_singleton()->get_ticks_usec(); while (OS::get_singleton()->get_ticks_usec() - time < wait) { OS::get_singleton()->delay_usec(1000 * sleep); - if (ea->quit_request) + if (ea->quit_request) { break; + } } } @@ -406,7 +427,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } String get_project_name(const String &p_name) const { - String aname; if (p_name != "") { aname = p_name; @@ -422,7 +442,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } String get_package_name(const String &p_package) const { - String pname = p_package; String basename = ProjectSettings::get_singleton()->get("application/config/name"); basename = basename.to_lower(); @@ -439,8 +458,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { first = false; } } - if (name == "") + if (name == "") { name = "noname"; + } pname = pname.replace("$genname", name); @@ -448,7 +468,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; if (pname.length() == 0) { @@ -512,7 +531,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* * By not compressing files with little or not benefit in doing so, * a performance gain is expected attime. Moreover, if the APK is @@ -559,7 +577,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } static zip_fileinfo get_zip_fileinfo() { - OS::Time time = OS::get_singleton()->get_time(); OS::Date date = OS::get_singleton()->get_date(); @@ -586,6 +603,73 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { return abis; } + /// List the gdap files in the directory specified by the p_path parameter. + static Vector<String> list_gdap_files(const String &p_path) { + Vector<String> dir_files; + DirAccessRef da = DirAccess::open(p_path); + if (da) { + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + + if (da->current_is_dir() || da->current_is_hidden()) { + continue; + } + + if (file.ends_with(PLUGIN_CONFIG_EXT)) { + dir_files.push_back(file); + } + } + da->list_dir_end(); + } + + return dir_files; + } + + static Vector<PluginConfig> get_plugins() { + Vector<PluginConfig> loaded_plugins; + + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); + + // Add the prebuilt plugins + loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir)); + + if (DirAccess::exists(plugins_dir)) { + Vector<String> plugins_filenames = list_gdap_files(plugins_dir); + + if (!plugins_filenames.empty()) { + Ref<ConfigFile> config_file = memnew(ConfigFile); + for (int i = 0; i < plugins_filenames.size(); i++) { + PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + if (config.valid_config) { + loaded_plugins.push_back(config); + } else { + print_error("Invalid plugin config file " + plugins_filenames[i]); + } + } + } + } + + return loaded_plugins; + } + + static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { + Vector<PluginConfig> enabled_plugins; + Vector<PluginConfig> all_plugins = get_plugins(); + for (int i = 0; i < all_plugins.size(); i++) { + PluginConfig plugin = all_plugins[i]; + bool enabled = p_presets->get("plugins/" + plugin.name); + if (enabled) { + enabled_plugins.push_back(plugin); + } + } + + return enabled_plugins; + } + static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) { zip_fileinfo zipfi = get_zip_fileinfo(); zipOpenNewFileInZip(ed->apk, @@ -648,7 +732,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { - // Leaving the unused types commented because looking these constants up // again later would be annoying // const int CHUNK_AXML_FILE = 0x00080003; @@ -688,16 +771,16 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { int xr_mode_index = p_preset->get("xr_features/xr_mode"); - String plugins = p_preset->get("custom_template/plugins"); + String plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); Vector<String> perms; const char **aperms = android_perms; while (*aperms) { - bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); - if (enabled) + if (enabled) { perms.push_back("android.permission." + String(*aperms)); + } aperms++; } @@ -711,19 +794,17 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } if (p_give_internet) { - if (perms.find("android.permission.INTERNET") == -1) + if (perms.find("android.permission.INTERNET") == -1) { perms.push_back("android.permission.INTERNET"); + } } while (ofs < (uint32_t)p_manifest.size()) { - uint32_t chunk = decode_uint32(&p_manifest[ofs]); uint32_t size = decode_uint32(&p_manifest[ofs + 4]); switch (chunk) { - case CHUNK_STRINGS: { - int iofs = ofs + 8; string_count = decode_uint32(&p_manifest[iofs]); @@ -744,14 +825,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { string_table_begins = st_offset; for (uint32_t i = 0; i < string_count; i++) { - uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]); string_at += st_offset + string_count * 4; ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table."); if (string_flags & UTF8_FLAG) { - } else { uint32_t len = decode_uint16(&p_manifest[string_at]); Vector<CharType> ucstring; @@ -774,7 +853,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } break; case CHUNK_XML_START_TAG: { - int iofs = ofs + 8; uint32_t name = decode_uint32(&p_manifest[iofs + 12]); @@ -804,8 +882,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (tname == "manifest" && attrname == "versionName") { if (attr_value == 0xFFFFFFFF) { WARN_PRINT("Version name in a resource, should be plain text"); - } else + } else { string_table.write[attr_value] = version_name; + } } if (tname == "instrumentation" && attrname == "targetPackage") { @@ -813,26 +892,20 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } if (tname == "activity" && attrname == "screenOrientation") { - encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]); } if (tname == "supports-screens") { - if (attrname == "smallScreens") { - encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "normalScreens") { - encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "largeScreens") { - encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "xlargeScreens") { - encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } } @@ -853,9 +926,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } } - if (tname == "meta-data" && attrname == "value" && value == "custom_template_plugins_value") { + if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) { // Update the meta-data 'android:value' attribute with the list of enabled plugins. - string_table.write[attr_value] = plugins; + string_table.write[attr_value] = plugins_names; } iofs += 20; @@ -889,8 +962,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { feature_required_list.push_back(hand_tracking_index == 2); feature_versions.push_back(-1); // no version attribute should be added. - if (perms.find("oculus.permission.handtracking") == -1) { - perms.push_back("oculus.permission.handtracking"); + if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) { + perms.push_back("com.oculus.permission.HAND_TRACKING"); } } } @@ -1037,7 +1110,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } } if (tname == "manifest") { - // save manifest ending so we can restore it Vector<uint8_t> manifest_end; uint32_t manifest_cur_size = p_manifest.size(); @@ -1123,13 +1195,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { ret.resize(string_table_begins + string_table.size() * 4); for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = p_manifest[i]; } ofs = 0; for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); ofs += string_table[i].length() * 2 + 2 + 2; } @@ -1138,7 +1208,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { string_data_offset = ret.size() - ofs; uint8_t *chars = &ret.write[string_data_offset]; for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; encode_uint16(s.length(), chars); chars += 2; @@ -1155,18 +1224,21 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } //pad - while (ret.size() % 4) + while (ret.size() % 4) { ret.push_back(0); + } uint32_t new_stable_end = ret.size(); uint32_t extra = (p_manifest.size() - string_table_ends); ret.resize(new_stable_end + extra); - for (uint32_t i = 0; i < extra; i++) + for (uint32_t i = 0; i < extra; i++) { ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; + } - while (ret.size() % 4) + while (ret.size() % 4) { ret.push_back(0); + } encode_uint32(ret.size(), &ret.write[4]); //update new file size encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size @@ -1177,19 +1249,36 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } static String _parse_string(const uint8_t *p_bytes, bool p_utf8) { - uint32_t offset = 0; - uint32_t len = decode_uint16(&p_bytes[offset]); + uint32_t len = 0; if (p_utf8) { - //don't know how to read extended utf8, this will have to be for now - len >>= 8; + uint8_t byte = p_bytes[offset]; + if (byte & 0x80) { + offset += 2; + } else { + offset += 1; + } + byte = p_bytes[offset]; + offset++; + if (byte & 0x80) { + len = byte & 0x7F; + len = (len << 8) + p_bytes[offset]; + offset++; + } else { + len = byte; + } + } else { + len = decode_uint16(&p_bytes[offset]); + offset += 2; + if (len & 0x8000) { + len &= 0x7FFF; + len = (len << 16) + decode_uint16(&p_bytes[offset]); + offset += 2; + } } - offset += 2; - //printf("len %i, unicode: %i\n",len,int(p_utf8)); if (p_utf8) { - Vector<uint8_t> str8; str8.resize(len + 1); for (uint32_t i = 0; i < len; i++) { @@ -1200,19 +1289,18 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { str.parse_utf8((const char *)str8.ptr()); return str; } else { - String str; for (uint32_t i = 0; i < len; i++) { CharType c = decode_uint16(&p_bytes[offset + i * 2]); - if (c == 0) + if (c == 0) { break; + } str += String::chr(c); } return str; } } void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) { - const int UTF8_FLAG = 0x00000100; uint32_t string_block_len = decode_uint32(&p_manifest[16]); @@ -1225,20 +1313,17 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String package_name = p_preset->get("package/name"); for (uint32_t i = 0; i < string_count; i++) { - uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]); offset += string_table_begins + string_count * 4; String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG); if (str.begins_with("godot-project-name")) { - if (str == "godot-project-name") { //project name str = get_project_name(package_name); } else { - String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_"); String prop = "application/config/name_" + lang; if (ProjectSettings::get_singleton()->has_setting(prop)) { @@ -1257,13 +1342,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { ret.resize(string_table_begins + string_table.size() * 4); for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = p_manifest[i]; } int ofs = 0; for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); ofs += string_table[i].length() * 2 + 2 + 2; } @@ -1271,7 +1354,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { ret.resize(ret.size() + ofs); uint8_t *chars = &ret.write[ret.size() - ofs]; for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; encode_uint16(s.length(), chars); chars += 2; @@ -1284,8 +1366,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } //pad - while (ret.size() % 4) + while (ret.size() % 4) { ret.push_back(0); + } //change flags to not use utf8 encode_uint32(string_flags & ~0x100, &ret.write[28]); @@ -1344,7 +1427,6 @@ public: public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); if (driver == "GLES2") { r_features->push_back("etc"); @@ -1361,7 +1443,6 @@ public: } virtual void get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0)); @@ -1370,7 +1451,14 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/plugins", PROPERTY_HINT_PLACEHOLDER_TEXT, "Plugin1,Plugin2,..."), "")); + + Vector<PluginConfig> plugins_configs = get_plugins(); + for (int i = 0; i < plugins_configs.size(); i++) { + print_verbose("Found Android plugin " + plugins_configs[i].name); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); + } + plugins_changed = false; + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); @@ -1408,7 +1496,6 @@ public: const char **perms = android_perms; while (*perms) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); perms++; } @@ -1426,8 +1513,16 @@ public: return logo; } - virtual bool poll_export() { + virtual bool should_update_export_options() { + bool export_options_changed = plugins_changed; + if (export_options_changed) { + // don't clear unless we're reporting true, to avoid race + plugins_changed = false; + } + return export_options_changed; + } + virtual bool poll_export() { bool dc = devices_changed; if (dc) { // don't clear unless we're reporting true, to avoid race @@ -1437,25 +1532,21 @@ public: } virtual int get_options_count() const { - MutexLock lock(device_lock); return devices.size(); } virtual String get_options_tooltip() const { - return TTR("Select device from the list"); } virtual String get_option_label(int p_index) const { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); MutexLock lock(device_lock); return devices[p_index].name; } virtual String get_option_tooltip(int p_index) const { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); MutexLock lock(device_lock); String s = devices[p_index].description; @@ -1469,7 +1560,6 @@ public: } virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { - ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); String can_export_error; @@ -1493,8 +1583,9 @@ public: const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); const bool use_reverse = devices[p_device].api_level >= 21; - if (use_reverse) + if (use_reverse) { p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; + } String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk"); @@ -1553,7 +1644,6 @@ public: if (use_remote) { if (use_reverse) { - static const char *const msg = "--- Device API >= 21; debugging over USB ---"; EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); print_line(String(msg).to_upper()); @@ -1566,7 +1656,6 @@ public: OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv); if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { - int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); args.clear(); args.push_back("-s"); @@ -1580,7 +1669,6 @@ public: } if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { - int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); args.clear(); @@ -1594,7 +1682,6 @@ public: print_line("Reverse result2: " + itos(rv)); } } else { - static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); print_line(String(msg).to_upper()); @@ -1634,7 +1721,6 @@ public: } virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; bool valid = false; @@ -1668,7 +1754,6 @@ public: String adb = EditorSettings::get_singleton()->get("export/android/adb"); if (!FileAccess::exists(adb)) { - valid = false; err += TTR("ADB executable not configured in the Editor Settings.") + "\n"; } @@ -1676,7 +1761,6 @@ public: String js = EditorSettings::get_singleton()->get("export/android/jarsigner"); if (!FileAccess::exists(js)) { - valid = false; err += TTR("OpenJDK jarsigner not configured in the Editor Settings.") + "\n"; } @@ -1684,7 +1768,6 @@ public: String dk = p_preset->get("keystore/debug"); if (!FileAccess::exists(dk)) { - dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); if (!FileAccess::exists(dk)) { valid = false; @@ -1699,7 +1782,7 @@ public: valid = false; } else { Error errn; - DirAccessRef da = DirAccess::open(sdk_path.plus_file("tools"), &errn); + DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); if (errn != OK) { err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n"; valid = false; @@ -1707,7 +1790,6 @@ public: } if (!FileAccess::exists("res://android/build/build.gradle")) { - err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; valid = false; } @@ -1716,7 +1798,6 @@ public: bool apk_expansion = p_preset->get("apk_expansion/enable"); if (apk_expansion) { - String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); if (apk_expansion_pkey == "") { @@ -1730,7 +1811,6 @@ public: String pn_err; if (!is_package_name_valid(get_package_name(pn), &pn_err)) { - valid = false; err += TTR("Invalid package name:") + " " + pn_err + "\n"; } @@ -1752,7 +1832,6 @@ public: } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); String src_apk; @@ -1789,18 +1868,22 @@ public: #endif String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); build_command = build_path.plus_file(build_command); String package_name = get_package_name(p_preset->get("package/unique_name")); - String plugins = p_preset->get("custom_template/plugins"); + + Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset); + String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins); + String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins); + String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins); List<String> cmdline; cmdline.push_back("build"); cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. - cmdline.push_back("-Pcustom_template_plugins_dir=" + plugins_dir); // argument to specify the plugins directory. - cmdline.push_back("-Pcustom_template_plugins=" + plugins); // argument to specify the list of plugins to enable. + cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. + cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. + cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. cmdline.push_back("-p"); // argument to specify the start directory. cmdline.push_back(build_path); // start directory. /*{ used for debug @@ -1827,11 +1910,11 @@ public: } } else { - - if (p_debug) + if (p_debug) { src_apk = p_preset->get("custom_template/debug"); - else + } else { src_apk = p_preset->get("custom_template/release"); + } src_apk = src_apk.strip_edges(); if (src_apk == "") { @@ -1860,7 +1943,6 @@ public: unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); if (!pkg) { - EditorNode::add_io_error("Could not find template APK to export:\n" + src_apk); return ERR_FILE_NOT_FOUND; } @@ -1932,8 +2014,8 @@ public: ImageLoader::load_image(path, launcher_adaptive_icon_background_image); } + Vector<String> invalid_abis(enabled_abis); while (ret == UNZ_OK) { - //get filename unz_file_info info; char fname[16384]; @@ -1977,6 +2059,7 @@ public: bool enabled = false; for (int i = 0; i < enabled_abis.size(); ++i) { if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + invalid_abis.erase(enabled_abis[i]); enabled = true; break; } @@ -2016,6 +2099,13 @@ public: ret = unzGoToNextFile(pkg); } + if (!invalid_abis.empty()) { + String unsupported_arch = String(", ").join(invalid_abis); + EditorNode::add_io_error("Missing libraries in the export template for the selected architectures: " + unsupported_arch + ".\n" + + "Please build a template with all required libraries, or uncheck the missing architectures in the export preset."); + CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); + } + if (ep.step("Adding files...", 1)) { CLEANUP_AND_RETURN(ERR_SKIP); } @@ -2031,7 +2121,6 @@ public: gen_export_flags(cl, p_flags); if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { - APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; @@ -2040,7 +2129,6 @@ public: //all files if (apk_expansion) { - String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; String fullpath = p_path.get_base_dir().plus_file(apkfname); err = save_pack(p_preset, fullpath); @@ -2059,7 +2147,6 @@ public: cl.push_back(apk_expansion_pkey.strip_edges()); } else { - APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; @@ -2076,14 +2163,17 @@ public: cl.push_back("--xr_mode_regular"); } - if (use_32_fb) + if (use_32_fb) { cl.push_back("--use_depth_32"); + } - if (immersive) + if (immersive) { cl.push_back("--use_immersive"); + } - if (debug_opengl) + if (debug_opengl) { cl.push_back("--debug_opengl"); + } if (cl.size()) { //add comandline @@ -2091,13 +2181,13 @@ public: clf.resize(4); encode_uint32(cl.size(), &clf.write[0]); for (int i = 0; i < cl.size(); i++) { - print_line(itos(i) + " param: " + cl[i]); CharString txt = cl[i].utf8(); int base = clf.size(); int length = txt.length(); - if (!length) + if (!length) { continue; + } clf.resize(base + 4 + length); encode_uint32(length, &clf.write[base]); copymem(&clf.write[base + 4], txt.ptr(), length); @@ -2128,7 +2218,6 @@ public: } if (_signed) { - String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner"); if (!FileAccess::exists(jarsigner)) { EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned."); @@ -2139,13 +2228,11 @@ public: String password; String user; if (p_debug) { - keystore = p_preset->get("keystore/debug"); password = p_preset->get("keystore/debug_password"); user = p_preset->get("keystore/debug_user"); if (keystore.empty()) { - keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); @@ -2222,7 +2309,6 @@ public: unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io); if (!tmp_unaligned) { - EditorNode::add_io_error("Could not unzip temporary unaligned APK."); CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); } @@ -2239,7 +2325,6 @@ public: // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp int bias = 0; while (ret == UNZ_OK) { - unz_file_info info; memset(&info, 0, sizeof(info)); @@ -2298,7 +2383,6 @@ public: } virtual void get_platform_features(List<String> *r_features) { - r_features->push_back("mobile"); r_features->push_back("Android"); } @@ -2307,7 +2391,6 @@ public: } EditorExportPlatformAndroid() { - Ref<Image> img = memnew(Image(_android_logo)); logo.instance(); logo->create_from_image(img); @@ -2317,19 +2400,19 @@ public: run_icon->create_from_image(img); devices_changed = true; + plugins_changed = true; quit_request = false; - device_thread = Thread::create(_device_poll_thread, this); + check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this); } ~EditorExportPlatformAndroid() { quit_request = true; - Thread::wait_to_finish(device_thread); - memdelete(device_thread); + Thread::wait_to_finish(check_for_changes_thread); + memdelete(check_for_changes_thread); } }; void register_android_exporter() { - String exe_ext; if (OS::get_singleton()->get_name() == "Windows") { exe_ext = "*.exe"; diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index fa805ec4f3..05d5fb576d 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -39,12 +39,10 @@ AAssetManager *FileAccessAndroid::asset_manager = nullptr; }*/ FileAccess *FileAccessAndroid::create_android() { - return memnew(FileAccessAndroid); } Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) { - String path = fix_path(p_path).simplify_path(); if (path.begins_with("/")) path = path.substr(1, path.length()); @@ -64,7 +62,6 @@ Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) { } void FileAccessAndroid::close() { - if (!a) return; AAsset_close(a); @@ -72,12 +69,10 @@ void FileAccessAndroid::close() { } bool FileAccessAndroid::is_open() const { - return a != nullptr; } void FileAccessAndroid::seek(size_t p_position) { - ERR_FAIL_COND(!a); AAsset_seek(a, p_position, SEEK_SET); pos = p_position; @@ -90,29 +85,24 @@ void FileAccessAndroid::seek(size_t p_position) { } void FileAccessAndroid::seek_end(int64_t p_position) { - ERR_FAIL_COND(!a); AAsset_seek(a, p_position, SEEK_END); pos = len + p_position; } size_t FileAccessAndroid::get_position() const { - return pos; } size_t FileAccessAndroid::get_len() const { - return len; } bool FileAccessAndroid::eof_reached() const { - return eof; } uint8_t FileAccessAndroid::get_8() const { - if (pos >= len) { eof = true; return 0; @@ -125,7 +115,6 @@ uint8_t FileAccessAndroid::get_8() const { } int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { - off_t r = AAsset_read(a, p_dst, p_length); if (pos + p_length > len) { @@ -133,7 +122,6 @@ int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { } if (r >= 0) { - pos += r; if (pos > len) { pos = len; @@ -143,22 +131,18 @@ int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessAndroid::get_error() const { - return eof ? ERR_FILE_EOF : OK; //not sure what else it may happen } void FileAccessAndroid::flush() { - ERR_FAIL(); } void FileAccessAndroid::store_8(uint8_t p_dest) { - ERR_FAIL(); } 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()); diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index 6b5ec541fd..a347c63ffb 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -38,7 +38,6 @@ //#include <android_native_app_glue.h> class FileAccessAndroid : public FileAccess { - static FileAccess *create_android(); mutable AAsset *a; mutable size_t len; diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index e088eca8ef..df8b57fd3a 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -44,12 +44,10 @@ jmethodID FileAccessJAndroid::_file_eof = 0; jmethodID FileAccessJAndroid::_file_close = 0; FileAccess *FileAccessJAndroid::create_jandroid() { - return memnew(FileAccessJAndroid); } Error FileAccessJAndroid::_open(const String &p_path, int p_mode_flags) { - if (is_open()) close(); @@ -75,7 +73,6 @@ Error FileAccessJAndroid::_open(const String &p_path, int p_mode_flags) { } void FileAccessJAndroid::close() { - if (!is_open()) return; @@ -86,12 +83,10 @@ void FileAccessJAndroid::close() { } bool FileAccessJAndroid::is_open() const { - return id != 0; } void FileAccessJAndroid::seek(size_t p_position) { - JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); @@ -99,42 +94,37 @@ void FileAccessJAndroid::seek(size_t p_position) { } void FileAccessJAndroid::seek_end(int64_t p_position) { - ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); seek(get_len()); } size_t FileAccessJAndroid::get_position() const { - JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); return env->CallIntMethod(io, _file_tell, id); } size_t FileAccessJAndroid::get_len() const { - JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); return env->CallIntMethod(io, _file_get_size, id); } bool FileAccessJAndroid::eof_reached() const { - JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); return env->CallIntMethod(io, _file_eof, id); } uint8_t FileAccessJAndroid::get_8() const { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); uint8_t byte; get_buffer(&byte, 1); return byte; } -int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const { +int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); if (p_length == 0) return 0; @@ -150,7 +140,6 @@ int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessJAndroid::get_error() const { - if (eof_reached()) return ERR_FILE_EOF; return OK; @@ -163,7 +152,6 @@ void FileAccessJAndroid::store_8(uint8_t p_dest) { } bool FileAccessJAndroid::file_exists(const String &p_path) { - JNIEnv *env = ThreadAndroid::get_env(); String path = fix_path(p_path).simplify_path(); @@ -184,7 +172,6 @@ bool FileAccessJAndroid::file_exists(const String &p_path) { } void FileAccessJAndroid::setup(jobject p_io) { - io = p_io; JNIEnv *env = ThreadAndroid::get_env(); @@ -201,12 +188,10 @@ void FileAccessJAndroid::setup(jobject p_io) { } FileAccessJAndroid::FileAccessJAndroid() { - id = 0; } FileAccessJAndroid::~FileAccessJAndroid() { - if (is_open()) close(); } diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index b361c64922..e252a4d3ac 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -34,7 +34,6 @@ #include "core/os/file_access.h" #include "java_godot_lib_jni.h" class FileAccessJAndroid : public FileAccess { - static jobject io; static jclass cls; diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index cc480d1c84..f5b1d29f22 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -32,8 +32,8 @@ <!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. --> <meta-data - android:name="custom_template_plugins" - android:value="custom_template_plugins_value"/> + android:name="plugins" + android:value="plugins_value"/> <activity android:name=".GodotApp" diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 1a3bb77670..ea341b37b1 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -21,6 +21,16 @@ allprojects { mavenCentral() google() jcenter() + + // Godot user plugins custom maven repos + String[] mavenRepos = getGodotPluginsMavenRepos() + if (mavenRepos != null && mavenRepos.size() > 0) { + for (String repoUrl : mavenRepos) { + maven { + url repoUrl + } + } + } } } @@ -40,15 +50,18 @@ dependencies { releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar']) } - // Godot prebuilt plugins - implementation fileTree(dir: 'libs/plugins', include: ["GodotPayment*.aar"]) + // Godot user plugins remote dependencies + String[] remoteDeps = getGodotPluginsRemoteBinaries() + if (remoteDeps != null && remoteDeps.size() > 0) { + for (String dep : remoteDeps) { + implementation dep + } + } - // Godot user plugins dependencies - String pluginsDir = getGodotPluginsDirectory() - String[] pluginsBinaries = getGodotPluginsBinaries() - if (pluginsDir != null && !pluginsDir.isEmpty() && - pluginsBinaries != null && pluginsBinaries.size() > 0) { - implementation fileTree(dir: pluginsDir, include: pluginsBinaries) + // Godot user plugins local dependencies + String[] pluginsBinaries = getGodotPluginsLocalBinaries() + if (pluginsBinaries != null && pluginsBinaries.size() > 0) { + implementation files(pluginsBinaries) } } @@ -56,6 +69,11 @@ android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + defaultConfig { // Feel free to modify the application id to your own. applicationId getExportPackageName() @@ -71,7 +89,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - doNotStrip '**/*.so' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } // Both signing and zip-aligning will be done at export time diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 96cfd272f2..5251bc3066 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -3,19 +3,19 @@ ext.versions = [ compileSdk : 29, minSdk : 18, targetSdk : 29, - buildTools : '29.0.1', - supportCoreUtils : '28.0.0', + buildTools : '29.0.3', + supportCoreUtils : '1.0.0', kotlinVersion : '1.3.61', - v4Support : '28.0.0' + v4Support : '1.0.0' ] ext.libraries = [ androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin", - supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils", + supportCoreUtils : "androidx.legacy:legacy-support-core-utils:$versions.supportCoreUtils", kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion", kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion", - v4Support : "com.android.support:support-v4:$versions.v4Support" + v4Support : "androidx.legacy:legacy-support-v4:$versions.v4Support" ] ext.getExportPackageName = { -> @@ -28,39 +28,68 @@ ext.getExportPackageName = { -> return appId } +final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|" + /** - * Parse the project properties for the 'custom_template_plugins' property and return - * their binaries for inclusion in the build dependencies. - * - * The listed plugins must have their binaries in the project plugins directory. + * Parse the project properties for the 'plugins_maven_repos' property and return the list + * of maven repos. */ -ext.getGodotPluginsBinaries = { -> - String[] binDeps = [] +ext.getGodotPluginsMavenRepos = { -> + Set<String> mavenRepos = [] - // Retrieve the list of enabled plugins. - if (project.hasProperty("custom_template_plugins")) { - String pluginsList = project.property("custom_template_plugins") - if (pluginsList != null && !pluginsList.trim().isEmpty()) { - for (String plugin : pluginsList.split(",")) { - binDeps += plugin.trim() + "*.aar" + // Retrieve the list of maven repos. + if (project.hasProperty("plugins_maven_repos")) { + String mavenReposProperty = project.property("plugins_maven_repos") + if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) { + for (String mavenRepoUrl : mavenReposProperty.split(PLUGIN_VALUE_SEPARATOR_REGEX)) { + mavenRepos += mavenRepoUrl.trim() } } } - return binDeps + return mavenRepos +} + +/** + * Parse the project properties for the 'plugins_remote_binaries' property and return + * it for inclusion in the build dependencies. + */ +ext.getGodotPluginsRemoteBinaries = { -> + Set<String> remoteDeps = [] + + // Retrieve the list of remote plugins binaries. + if (project.hasProperty("plugins_remote_binaries")) { + String remoteDepsList = project.property("plugins_remote_binaries") + if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) { + for (String dep: remoteDepsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) { + remoteDeps += dep.trim() + } + } + } + return remoteDeps } /** - * Parse the project properties for the 'custom_template_plugins_dir' property and return - * its value. + * Parse the project properties for the 'plugins_local_binaries' property and return + * their binaries for inclusion in the build dependencies. * - * The returned value is the directory containing user plugins. + * Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable. */ -ext.getGodotPluginsDirectory = { -> - // The plugins directory is provided by the 'custom_template_plugins_dir' property. - String pluginsDir = project.hasProperty("custom_template_plugins_dir") - ? project.property("custom_template_plugins_dir") - : "" +ext.getGodotPluginsLocalBinaries = { -> + // Set the prebuilt plugins as default. If custom build is enabled, + // the 'plugins_local_binaries' will be defined so we can use it instead. + Set<String> binDeps = ["libs/plugins/GodotPayment.release.aar"] + + // Retrieve the list of local plugins binaries. + if (project.hasProperty("plugins_local_binaries")) { + binDeps.clear() + String pluginsList = project.property("plugins_local_binaries") + if (pluginsList != null && !pluginsList.trim().isEmpty()) { + for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) { + binDeps += plugin.trim() + } + } + } - return pluginsDir + return binDeps } diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle new file mode 100644 index 0000000000..33b863c7bf --- /dev/null +++ b/platform/android/java/app/settings.gradle @@ -0,0 +1,2 @@ +// Empty settings.gradle file to denote this directory as being the root project +// of the Godot custom build. diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 865b61956c..01a3607b20 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -140,7 +140,7 @@ task generateGodotTemplates(type: GradleBuild) { startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType) } - tasks = ["copyGodotPaymentPluginToAppModule"] + tasks = [] // Only build the apks and aar files for which we have native shared libraries. for (String target : supportedTargets) { @@ -161,6 +161,7 @@ task generateGodotTemplates(type: GradleBuild) { } } + dependsOn 'copyGodotPaymentPluginToAppModule' finalizedBy 'zipCustomBuild' } diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties index aac7c9b461..e14cd5ba5c 100644 --- a/platform/android/java/gradle.properties +++ b/platform/android/java/gradle.properties @@ -7,6 +7,9 @@ # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html +android.enableJetifier=true +android.useAndroidX=true + # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index b133585f99..fa39bc0f1d 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -13,7 +13,7 @@ <instrumentation android:icon="@mipmap/icon" android:label="@string/godot_project_name_string" - android:name=".GodotInstrumentation" + android:name="org.godotengine.godot.GodotInstrumentation" android:targetPackage="org.godotengine.godot" /> </manifest> diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 062f91e08e..19eee5a315 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' dependencies { implementation libraries.supportCoreUtils @@ -11,7 +12,6 @@ def pathToRootDir = "../../../../" android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools - useLibrary 'org.apache.http.legacy' defaultConfig { minSdkVersion versions.minSdk @@ -26,7 +26,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - doNotStrip '**/*.so' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } sourceSets { diff --git a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index f849d8e90d..0000000000 --- a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 1dfb28b33a..0000000000 --- a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 372b763ec5..0000000000 --- a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 302a972049..0000000000 --- a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java index 0abaf2e052..d481c22204 100644 --- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java +++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java @@ -29,9 +29,9 @@ import com.google.android.vending.expansion.downloader.IDownloaderClient; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; -import android.os.Build; import android.os.Messenger; -import android.support.v4.app.NotificationCompat; + +import androidx.core.app.NotificationCompat; /** * This class handles displaying the notification associated with the download diff --git a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java index 594cae774b..8b7a9c6c74 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java @@ -34,16 +34,13 @@ import java.util.HashMap; import java.util.Set; public class Dictionary extends HashMap<String, Object> { - protected String[] keys_cache; public String[] get_keys() { - String[] ret = new String[size()]; int i = 0; Set<String> keys = keySet(); for (String key : keys) { - ret[i] = key; i++; }; @@ -52,12 +49,10 @@ public class Dictionary extends HashMap<String, Object> { }; public Object[] get_values() { - Object[] ret = new Object[size()]; int i = 0; Set<String> keys = keySet(); for (String key : keys) { - ret[i] = get(key); i++; }; @@ -70,7 +65,6 @@ public class Dictionary extends HashMap<String, Object> { }; public void set_values(Object[] vals) { - int i = 0; for (String key : keys_cache) { put(key, vals[i]); diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 1798a1df3a..f27d8620ec 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -30,6 +30,13 @@ package org.godotengine.godot; +import org.godotengine.godot.input.GodotEditText; +import org.godotengine.godot.plugin.GodotPlugin; +import org.godotengine.godot.plugin.GodotPluginRegistry; +import org.godotengine.godot.utils.GodotNetUtils; +import org.godotengine.godot.utils.PermissionsUtil; +import org.godotengine.godot.xr.XRMode; + import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; @@ -59,10 +66,6 @@ import android.os.Messenger; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings.Secure; -import android.support.annotation.CallSuper; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.v4.app.FragmentActivity; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; @@ -77,6 +80,12 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.TextView; + +import androidx.annotation.CallSuper; +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; + import com.google.android.vending.expansion.downloader.DownloadProgressInfo; import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; @@ -84,6 +93,7 @@ import com.google.android.vending.expansion.downloader.Helpers; import com.google.android.vending.expansion.downloader.IDownloaderClient; import com.google.android.vending.expansion.downloader.IDownloaderService; import com.google.android.vending.expansion.downloader.IStub; + import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @@ -91,15 +101,8 @@ import java.security.MessageDigest; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import org.godotengine.godot.input.GodotEditText; -import org.godotengine.godot.plugin.GodotPlugin; -import org.godotengine.godot.plugin.GodotPluginRegistry; -import org.godotengine.godot.utils.GodotNetUtils; -import org.godotengine.godot.utils.PermissionsUtil; -import org.godotengine.godot.xr.XRMode; public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient { - private IStub mDownloaderClientStub; private TextView mStatusText; private TextView mProgressFraction; @@ -153,7 +156,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe private String[] command_line; private boolean use_apk_expansion; - public GodotView mView; + public GodotRenderView mRenderView; private boolean godot_initialized = false; private SensorManager mSensorManager; @@ -213,35 +216,41 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe setContentView(layout); // GodotEditText layout - GodotEditText edittext = new GodotEditText(this); - edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + GodotEditText editText = new GodotEditText(this); + editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); // ...add to FrameLayout - layout.addView(edittext); + layout.addView(editText); - mView = new GodotView(this, xrMode, use_32_bits, use_debug_opengl); - layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - edittext.setView(mView); - io.setEdit(edittext); + GodotLib.setup(command_line); + + final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name"); + if (videoDriver.equals("Vulkan")) { + mRenderView = new GodotVulkanRenderView(this); + } else { + mRenderView = new GodotGLRenderView(this, xrMode, use_32_bits, use_debug_opengl); + } - mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + View view = mRenderView.getView(); + layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + editText.setView(mRenderView); + io.setEdit(editText); + + view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Point fullSize = new Point(); getWindowManager().getDefaultDisplay().getSize(fullSize); Rect gameSize = new Rect(); - mView.getWindowVisibleDisplayFrame(gameSize); + mRenderView.getView().getWindowVisibleDisplayFrame(gameSize); final int keyboardHeight = fullSize.y - gameSize.bottom; GodotLib.setVirtualKeyboardHeight(keyboardHeight); } }); - final String[] current_command_line = command_line; - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { - GodotLib.setup(current_command_line); - // Must occur after GodotLib.setup has completed. for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onRegisterPluginWithGodotNative(); @@ -351,7 +360,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe for (int i = 0; i < argc; i++) { r = is.read(len); if (r < 4) { - return new String[0]; } int strlen = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF)); @@ -384,7 +392,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe */ @Keep private Surface getSurface() { - return mView.getHolder().getSurface(); + return mRenderView.getView().getHolder().getSurface(); } /** @@ -399,9 +407,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe String expansion_pack_path; private void initializeGodot() { - if (expansion_pack_path != null) { - String[] new_cmdline; int cll = 0; if (command_line != null) { @@ -448,7 +454,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe @Override protected void onCreate(Bundle icicle) { - super.onCreate(icicle); Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); @@ -465,7 +470,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe List<String> new_args = new LinkedList<String>(); for (int i = 0; i < command_line.length; i++) { - boolean has_extra = i < command_line.length - 1; if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) { xrMode = XRMode.REGULAR; @@ -509,7 +513,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe if (new_args.isEmpty()) { command_line = null; } else { - command_line = new_args.toArray(new String[new_args.size()]); } if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) { @@ -531,7 +534,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe boolean pack_valid = true; if (!f.exists()) { - pack_valid = false; } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) { @@ -543,7 +545,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } if (!pack_valid) { - Intent notifierIntent = new Intent(this, this.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -592,7 +593,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe @Override protected void onDestroy() { - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onMainDestroy(); } @@ -617,7 +617,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } return; } - mView.onPause(); + mRenderView.onActivityPaused(); mSensorManager.unregisterListener(this); @@ -627,7 +627,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } public String getClipboard() { - String copiedText = ""; if (mClipboard.getPrimaryClip() != null) { @@ -639,7 +638,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } public void setClipboard(String p_text) { - ClipData clip = ClipData.newPlainText("myLabel", p_text); mClipboard.setPrimaryClip(clip); } @@ -655,7 +653,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe return; } - mView.onResume(); + mRenderView.onActivityResumed(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME); @@ -721,8 +719,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe final float z = adjustedValues[2]; final int typeOfSensor = event.sensor.getType(); - if (mView != null) { - mView.queueEvent(new Runnable() { + if (mRenderView != null) { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { @@ -773,8 +771,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } } - if (shouldQuit && mView != null) { - mView.queueEvent(new Runnable() { + if (shouldQuit && mRenderView != null) { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { GodotLib.back(); @@ -789,8 +787,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe * This must be called after the render thread has started. */ public final void runOnRenderThread(@NonNull Runnable action) { - if (mView != null) { - mView.queueEvent(action); + if (mRenderView != null) { + mRenderView.queueOnRenderThread(action); } } @@ -799,9 +797,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } private boolean obbIsCorrupted(String f, String main_pack_md5) { - try { - InputStream fis = new FileInputStream(f); // Create MD5 Hash @@ -842,16 +838,14 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } public boolean gotTouchEvent(final MotionEvent event) { - final int evcount = event.getPointerCount(); if (evcount == 0) return true; - if (mView != null) { + if (mRenderView != null) { final int[] arr = new int[event.getPointerCount() * 3]; for (int i = 0; i < event.getPointerCount(); i++) { - arr[i * 3 + 0] = (int)event.getPointerId(i); arr[i * 3 + 1] = (int)event.getX(i); arr[i * 3 + 2] = (int)event.getY(i); @@ -860,7 +854,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe //System.out.printf("gaction: %d\n",event.getAction()); final int action = event.getAction() & MotionEvent.ACTION_MASK; - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { switch (action) { @@ -910,8 +904,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe int cnt = 0; for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) ; - if (cnt == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event); - mView.queueEvent(new Runnable() { + if (cnt == 0) + return super.onKeyMultiple(inKeyCode, repeatCount, event); + mRenderView.queueOnRenderThread(new Runnable() { // This method will be called on the rendering thread: public void run() { for (int i = 0, n = cc.length; i < n; i++) { @@ -1033,6 +1028,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe progress.mOverallTotal)); } public void initInputDevices() { - mView.initInputDevices(); + mRenderView.initInputDevices(); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java index 1fb242d0bc..a3dae15980 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; + import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; /** @@ -45,7 +46,6 @@ import com.google.android.vending.expansion.downloader.DownloaderClientMarshalle * <receiver android:name=".GodotDownloaderAlarmReceiver"/> */ public class GodotDownloaderAlarmReceiver extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { Log.d("GODOT", "Alarma recivida"); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java index 7e74e8a80d..434da95bc0 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java @@ -33,6 +33,7 @@ package org.godotengine.godot; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; + import com.google.android.vending.expansion.downloader.impl.DownloaderService; /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 8d3c2ae319..14dd893faa 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -1,5 +1,5 @@ /*************************************************************************/ -/* GodotView.java */ +/* GodotGLRenderView.java */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -29,12 +29,7 @@ /*************************************************************************/ package org.godotengine.godot; -import android.annotation.SuppressLint; -import android.graphics.PixelFormat; -import android.opengl.GLSurfaceView; -import android.view.GestureDetector; -import android.view.KeyEvent; -import android.view.MotionEvent; + import org.godotengine.godot.input.GodotGestureHandler; import org.godotengine.godot.input.GodotInputHandler; import org.godotengine.godot.utils.GLUtils; @@ -46,6 +41,14 @@ import org.godotengine.godot.xr.regular.RegularConfigChooser; import org.godotengine.godot.xr.regular.RegularContextFactory; import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; +import android.annotation.SuppressLint; +import android.graphics.PixelFormat; +import android.opengl.GLSurfaceView; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceView; + /** * A simple GLSurfaceView sub-class that demonstrate how to perform * OpenGL ES 2.0 rendering into a GL Surface. Note the following important @@ -64,16 +67,13 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; * that matches it exactly (with regards to red/green/blue/alpha channels * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */ -public class GodotView extends GLSurfaceView { - - private static String TAG = GodotView.class.getSimpleName(); - +public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { private final Godot activity; private final GodotInputHandler inputHandler; private final GestureDetector detector; private final GodotRenderer godotRenderer; - public GodotView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) { + public GodotGLRenderView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) { super(activity); GLUtils.use_32 = p_use_32_bits; GLUtils.use_debug_opengl = p_use_debug_opengl; @@ -85,10 +85,36 @@ public class GodotView extends GLSurfaceView { init(xrMode, false, 16, 0); } + @Override + public SurfaceView getView() { + return this; + } + + @Override public void initInputDevices() { this.inputHandler.initInputDevices(); } + @Override + public void queueOnRenderThread(Runnable event) { + queueEvent(event); + } + + @Override + public void onActivityPaused() { + onPause(); + } + + @Override + public void onActivityResumed() { + onResume(); + } + + @Override + public void onBackPressed() { + activity.onBackPressed(); + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { @@ -113,11 +139,9 @@ public class GodotView extends GLSurfaceView { } private void init(XRMode xrMode, boolean translucent, int depth, int stencil) { - setPreserveEGLContextOnPause(true); setFocusableInTouchMode(true); switch (xrMode) { - case OVR: // Replace the default egl config chooser. setEGLConfigChooser(new OvrConfigChooser()); @@ -170,10 +194,6 @@ public class GodotView extends GLSurfaceView { setRenderer(godotRenderer); } - public void onBackPressed() { - activity.onBackPressed(); - } - @Override public void onResume() { super.onResume(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index 68ce40ba10..410b93824d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -29,6 +29,9 @@ /*************************************************************************/ package org.godotengine.godot; + +import org.godotengine.godot.input.*; + import android.content.*; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -39,22 +42,18 @@ import android.os.*; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; + import java.io.IOException; import java.io.InputStream; import java.util.Locale; -import org.godotengine.godot.input.*; -//android.os.Build // Wrapper for native library public class GodotIO { - AssetManager am; Godot activity; GodotEditText edit; - MediaPlayer mediaPlayer; - final int SCREEN_LANDSCAPE = 0; final int SCREEN_PORTRAIT = 1; final int SCREEN_REVERSE_LANDSCAPE = 2; @@ -70,7 +69,6 @@ public class GodotIO { public int last_file_id = 1; class AssetData { - public boolean eof = false; public String path; public InputStream is; @@ -81,7 +79,6 @@ public class GodotIO { SparseArray<AssetData> streams; public int file_open(String path, boolean write) { - //System.out.printf("file_open: Attempt to Open %s\n",path); //Log.v("MyApp", "TRYING TO OPEN FILE: " + path); @@ -94,7 +91,6 @@ public class GodotIO { ad.is = am.open(path); } catch (Exception e) { - //System.out.printf("Exception on file_open: %s\n",path); return -1; } @@ -102,7 +98,6 @@ public class GodotIO { try { ad.len = ad.is.available(); } catch (Exception e) { - System.out.printf("Exception availabling on file_open: %s\n", path); return -1; } @@ -115,7 +110,6 @@ public class GodotIO { return last_file_id; } public int file_get_size(int id) { - if (streams.get(id) == null) { System.out.printf("file_get_size: Invalid file id: %d\n", id); return -1; @@ -124,7 +118,6 @@ public class GodotIO { return streams.get(id).len; } public void file_seek(int id, int bytes) { - if (streams.get(id) == null) { System.out.printf("file_get_size: Invalid file id: %d\n", id); return; @@ -137,7 +130,6 @@ public class GodotIO { bytes = 0; try { - if (bytes > (int)ad.pos) { int todo = bytes - (int)ad.pos; while (todo > 0) { @@ -145,7 +137,6 @@ public class GodotIO { } ad.pos = bytes; } else if (bytes < (int)ad.pos) { - ad.is = am.open(ad.path); ad.pos = bytes; @@ -157,14 +148,12 @@ public class GodotIO { ad.eof = false; } catch (IOException e) { - System.out.printf("Exception on file_seek: %s\n", e); return; } } public int file_tell(int id) { - if (streams.get(id) == null) { System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id); return 0; @@ -174,7 +163,6 @@ public class GodotIO { return ad.pos; } public boolean file_eof(int id) { - if (streams.get(id) == null) { System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id); return false; @@ -185,7 +173,6 @@ public class GodotIO { } public byte[] file_read(int id, int bytes) { - if (streams.get(id) == null) { System.out.printf("file_read: Can't read invalid file id: %d\n", id); return new byte[0]; @@ -194,13 +181,11 @@ public class GodotIO { AssetData ad = streams.get(id); if (ad.pos + bytes > ad.len) { - bytes = ad.len - ad.pos; ad.eof = true; } if (bytes == 0) { - return new byte[0]; } @@ -209,7 +194,6 @@ public class GodotIO { try { r = ad.is.read(buf1); } catch (IOException e) { - System.out.printf("Exception on file_read: %s\n", e); return new byte[bytes]; } @@ -221,19 +205,16 @@ public class GodotIO { ad.pos += r; if (r < bytes) { - byte[] buf2 = new byte[r]; for (int i = 0; i < r; i++) buf2[i] = buf1[i]; return buf2; } else { - return buf1; } } public void file_close(int id) { - if (streams.get(id) == null) { System.out.printf("file_close: Can't close invalid file id: %d\n", id); return; @@ -247,7 +228,6 @@ public class GodotIO { ///////////////////////// class AssetDir { - public String[] files; public int current; public String path; @@ -258,7 +238,6 @@ public class GodotIO { SparseArray<AssetDir> dirs; public int dir_open(String path) { - AssetDir ad = new AssetDir(); ad.current = 0; ad.path = path; @@ -271,7 +250,6 @@ public class GodotIO { return -1; } } catch (IOException e) { - System.out.printf("Exception on dir_open: %s\n", e); return -1; } @@ -310,7 +288,6 @@ public class GodotIO { } public String dir_next(int id) { - if (dirs.get(id) == null) { System.out.printf("dir_next: invalid dir id: %d\n", id); return ""; @@ -329,7 +306,6 @@ public class GodotIO { } public void dir_close(int id) { - if (dirs.get(id) == null) { System.out.printf("dir_close: invalid dir id: %d\n", id); return; @@ -339,7 +315,6 @@ public class GodotIO { } GodotIO(Godot p_activity) { - am = p_activity.getAssets(); activity = p_activity; //streams = new HashMap<Integer, AssetData>(); @@ -430,7 +405,6 @@ public class GodotIO { } public void audioPause(boolean p_pause) { - if (p_pause) mAudioTrack.pause(); else @@ -442,7 +416,6 @@ public class GodotIO { ///////////////////////// public int openURI(String p_uri) { - try { Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); String path = p_uri; @@ -451,7 +424,6 @@ public class GodotIO { //absolute path to filesystem, prepend file:// path = "file://" + path; if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) { - type = "image/*"; } } @@ -467,18 +439,15 @@ public class GodotIO { activity.startActivity(intent); return 0; } catch (ActivityNotFoundException e) { - return 1; } } public String getDataDir() { - return activity.getFilesDir().getAbsolutePath(); } public String getLocale() { - return Locale.getDefault().toString(); } @@ -505,9 +474,7 @@ public class GodotIO { }; public void setScreenOrientation(int p_orientation) { - switch (p_orientation) { - case SCREEN_LANDSCAPE: { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } break; @@ -530,44 +497,14 @@ public class GodotIO { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); } break; } - }; - - public void setEdit(GodotEditText _edit) { - edit = _edit; } - public void playVideo(String p_path) { - Uri filePath = Uri.parse(p_path); - mediaPlayer = new MediaPlayer(); - - try { - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mediaPlayer.setDataSource(activity.getApplicationContext(), filePath); - mediaPlayer.prepare(); - mediaPlayer.start(); - } catch (IOException e) { - System.out.println("IOError while playing video"); - } - } - - public boolean isVideoPlaying() { - if (mediaPlayer != null) { - return mediaPlayer.isPlaying(); - } - return false; - } - - public void pauseVideo() { - if (mediaPlayer != null) { - mediaPlayer.pause(); - } + public int getScreenOrientation() { + return activity.getRequestedOrientation(); } - public void stopVideo() { - if (mediaPlayer != null) { - mediaPlayer.release(); - mediaPlayer = null; - } + public void setEdit(GodotEditText _edit) { + edit = _edit; } public static final int SYSTEM_DIR_DESKTOP = 0; @@ -580,7 +517,6 @@ public class GodotIO { public static final int SYSTEM_DIR_RINGTONES = 7; public String getSystemDir(int idx) { - String what = ""; switch (idx) { case SYSTEM_DIR_DESKTOP: { @@ -625,7 +561,6 @@ public class GodotIO { public static String unique_id = ""; public String getUniqueID() { - return unique_id; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 89a65aea24..72198ba123 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -32,6 +32,8 @@ package org.godotengine.godot; import android.app.Activity; import android.hardware.SensorEvent; +import android.view.Surface; + import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -39,7 +41,6 @@ import javax.microedition.khronos.opengles.GL10; * Wrapper for native library */ public class GodotLib { - public static GodotIO io; static { @@ -72,11 +73,11 @@ public class GodotLib { public static native void resize(int width, int height); /** - * Invoked on the GL thread when the underlying Android surface is created or recreated. + * Invoked on the render thread when the underlying Android surface is created or recreated. + * @param p_surface * @param p_32_bits - * @see android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig) */ - public static native void newcontext(boolean p_32_bits); + public static native void newcontext(Surface p_surface, boolean p_32_bits); /** * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread. diff --git a/platform/android/vulkan/vk_renderer_jni.h b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 017766fea2..27e63f3a66 100644 --- a/platform/android/vulkan/vk_renderer_jni.h +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -1,5 +1,5 @@ /*************************************************************************/ -/* vk_renderer_jni.h */ +/* GodotRenderView.java */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,19 +28,19 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VK_RENDERER_JNI_H -#define VK_RENDERER_JNI_H +package org.godotengine.godot; -#include <android/log.h> -#include <jni.h> +import android.view.SurfaceView; -extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj); -} +public interface GodotRenderView { + abstract public SurfaceView getView(); + + abstract public void initInputDevices(); + + abstract public void queueOnRenderThread(Runnable event); -#endif // VK_RENDERER_JNI_H + abstract public void onActivityPaused(); + abstract public void onActivityResumed(); + + abstract public void onBackPressed(); +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index ee9a2aee4f..99d3662317 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -30,19 +30,20 @@ package org.godotengine.godot; +import org.godotengine.godot.plugin.GodotPlugin; +import org.godotengine.godot.plugin.GodotPluginRegistry; +import org.godotengine.godot.utils.GLUtils; + import android.content.Context; import android.opengl.GLSurfaceView; + import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -import org.godotengine.godot.plugin.GodotPlugin; -import org.godotengine.godot.plugin.GodotPluginRegistry; -import org.godotengine.godot.utils.GLUtils; /** * Godot's renderer implementation. */ class GodotRenderer implements GLSurfaceView.Renderer { - private final GodotPluginRegistry pluginRegistry; private boolean activityJustResumed = false; @@ -70,7 +71,7 @@ class GodotRenderer implements GLSurfaceView.Renderer { } public void onSurfaceCreated(GL10 gl, EGLConfig config) { - GodotLib.newcontext(GLUtils.use_32); + GodotLib.newcontext(null, GLUtils.use_32); for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onGLSurfaceCreated(gl, config); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java new file mode 100644 index 0000000000..e9872b58ff --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -0,0 +1,142 @@ +/*************************************************************************/ +/* GodotVulkanRenderView.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot; + +import org.godotengine.godot.input.GodotGestureHandler; +import org.godotengine.godot.input.GodotInputHandler; +import org.godotengine.godot.vulkan.VkRenderer; +import org.godotengine.godot.vulkan.VkSurfaceView; + +import android.annotation.SuppressLint; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceView; + +public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { + private final Godot mActivity; + private final GodotInputHandler mInputHandler; + private final GestureDetector mGestureDetector; + private final VkRenderer mRenderer; + + public GodotVulkanRenderView(Godot activity) { + super(activity); + + mActivity = activity; + mInputHandler = new GodotInputHandler(this); + mGestureDetector = new GestureDetector(mActivity, new GodotGestureHandler(this)); + mRenderer = new VkRenderer(); + + setFocusableInTouchMode(true); + startRenderer(mRenderer); + } + + @Override + public SurfaceView getView() { + return this; + } + + @Override + public void initInputDevices() { + mInputHandler.initInputDevices(); + } + + @Override + public void queueOnRenderThread(Runnable event) { + queueOnVkThread(event); + } + + @Override + public void onActivityPaused() { + onPause(); + } + + @Override + public void onActivityResumed() { + onResume(); + } + + @Override + public void onBackPressed() { + mActivity.onBackPressed(); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); + return mActivity.gotTouchEvent(event); + } + + @Override + public boolean onKeyUp(final int keyCode, KeyEvent event) { + return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyDown(final int keyCode, KeyEvent event) { + return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); + } + + @Override + public void onResume() { + super.onResume(); + + queueOnVkThread(new Runnable() { + @Override + public void run() { + // Resume the renderer + mRenderer.onVkResume(); + GodotLib.focusin(); + } + }); + } + + @Override + public void onPause() { + super.onPause(); + + queueOnVkThread(new Runnable() { + @Override + public void run() { + GodotLib.focusout(); + // Pause the renderer + mRenderer.onVkPause(); + } + }); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index e901b4b36d..547c093419 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -29,6 +29,9 @@ /*************************************************************************/ package org.godotengine.godot.input; + +import org.godotengine.godot.*; + import android.content.Context; import android.os.Handler; import android.os.Message; @@ -38,8 +41,8 @@ import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; + import java.lang.ref.WeakReference; -import org.godotengine.godot.*; public class GodotEditText extends EditText { // =========================================================== @@ -51,7 +54,7 @@ public class GodotEditText extends EditText { // =========================================================== // Fields // =========================================================== - private GodotView mView; + private GodotRenderView mRenderView; private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; @@ -76,22 +79,22 @@ public class GodotEditText extends EditText { // =========================================================== public GodotEditText(final Context context) { super(context); - this.initView(); + initView(); } public GodotEditText(final Context context, final AttributeSet attrs) { super(context, attrs); - this.initView(); + initView(); } public GodotEditText(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - this.initView(); + initView(); } protected void initView() { - this.setPadding(0, 0, 0, 0); - this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + setPadding(0, 0, 0, 0); + setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); } private void handleMessage(final Message msg) { @@ -106,7 +109,7 @@ public class GodotEditText extends EditText { edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); setMaxInputLength(edit, msg.arg1); - final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(edit, 0); } } break; @@ -115,9 +118,9 @@ public class GodotEditText extends EditText { GodotEditText edit = (GodotEditText)msg.obj; edit.removeTextChangedListener(mInputWrapper); - final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(edit.getWindowToken(), 0); - edit.mView.requestFocus(); + edit.mRenderView.getView().requestFocus(); } break; } } @@ -135,12 +138,12 @@ public class GodotEditText extends EditText { // =========================================================== // Getter & Setter // =========================================================== - public void setView(final GodotView view) { - this.mView = view; + public void setView(final GodotRenderView view) { + mRenderView = view; if (mInputWrapper == null) - mInputWrapper = new GodotTextInputWrapper(mView, this); - this.setOnEditorActionListener(mInputWrapper); - view.requestFocus(); + mInputWrapper = new GodotTextInputWrapper(mRenderView, this); + setOnEditorActionListener(mInputWrapper); + view.getView().requestFocus(); } // =========================================================== @@ -152,7 +155,7 @@ public class GodotEditText extends EditText { /* Let GlSurfaceView get focus if back key is input. */ if (keyCode == KeyEvent.KEYCODE_BACK) { - this.mView.requestFocus(); + mRenderView.getView().requestFocus(); } return true; @@ -162,7 +165,7 @@ public class GodotEditText extends EditText { // Methods // =========================================================== public void showKeyboard(String p_existing_text, int p_max_input_length) { - this.mOriginText = p_existing_text; + mOriginText = p_existing_text; final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java index 1a38a9c3d2..1c9a683bbd 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java @@ -30,26 +30,26 @@ package org.godotengine.godot.input; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.GodotRenderView; + import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.GodotView; /** - * Handles gesture input related events for the {@link GodotView} view. + * Handles gesture input related events for the {@link GodotRenderView} view. * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener */ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener { + private final GodotRenderView mRenderView; - private final GodotView godotView; - - public GodotGestureHandler(GodotView godotView) { - this.godotView = godotView; + public GodotGestureHandler(GodotRenderView godotView) { + mRenderView = godotView; } private void queueEvent(Runnable task) { - godotView.queueEvent(task); + mRenderView.queueOnRenderThread(task); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index e00ca86c41..9abd65cc67 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -32,37 +32,38 @@ package org.godotengine.godot.input; import static org.godotengine.godot.utils.GLUtils.DEBUG; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.GodotRenderView; +import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; + import android.util.Log; import android.view.InputDevice; import android.view.InputDevice.MotionRange; import android.view.KeyEvent; import android.view.MotionEvent; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.GodotView; -import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; /** - * Handles input related events for the {@link GodotView} view. + * Handles input related events for the {@link GodotRenderView} view. */ public class GodotInputHandler implements InputDeviceListener { + private final ArrayList<Joystick> mJoysticksDevices = new ArrayList<Joystick>(); - private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>(); - - private final GodotView godotView; - private final InputManagerCompat inputManager; + private final GodotRenderView mRenderView; + private final InputManagerCompat mInputManager; - public GodotInputHandler(GodotView godotView) { - this.godotView = godotView; - this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext()); - this.inputManager.registerInputDeviceListener(this, null); + public GodotInputHandler(GodotRenderView godotView) { + mRenderView = godotView; + mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext()); + mInputManager.registerInputDeviceListener(this, null); } private void queueEvent(Runnable task) { - godotView.queueEvent(task); + mRenderView.queueOnRenderThread(task); } private boolean isKeyEvent_GameDevice(int source) { @@ -84,7 +85,6 @@ public class GodotInputHandler implements InputDeviceListener { int source = event.getSource(); if (isKeyEvent_GameDevice(source)) { - final int button = getGodotButton(keyCode); final int device_id = findJoystickDevice(event.getDeviceId()); @@ -113,7 +113,7 @@ public class GodotInputHandler implements InputDeviceListener { public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - godotView.onBackPressed(); + mRenderView.onBackPressed(); // press 'back' button should not terminate program //normal handle 'back' event in game logic return true; @@ -127,7 +127,6 @@ public class GodotInputHandler implements InputDeviceListener { //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD))); if (isKeyEvent_GameDevice(source)) { - if (event.getRepeatCount() > 0) // ignore key echo return true; @@ -159,12 +158,11 @@ public class GodotInputHandler implements InputDeviceListener { public boolean onGenericMotionEvent(MotionEvent event) { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { - final int device_id = findJoystickDevice(event.getDeviceId()); // Check if the device exists if (device_id > -1) { - Joystick joy = joysticksDevices.get(device_id); + Joystick joy = mJoysticksDevices.get(device_id); for (int i = 0; i < joy.axes.size(); i++) { InputDevice.MotionRange range = joy.axes.get(i); @@ -208,11 +206,11 @@ public class GodotInputHandler implements InputDeviceListener { public void initInputDevices() { /* initially add input devices*/ - int[] deviceIds = inputManager.getInputDeviceIds(); + int[] deviceIds = mInputManager.getInputDeviceIds(); for (int deviceId : deviceIds) { - InputDevice device = inputManager.getInputDevice(deviceId); + InputDevice device = mInputManager.getInputDevice(deviceId); if (DEBUG) { - Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); + Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); } onInputDeviceAdded(deviceId); } @@ -224,13 +222,13 @@ public class GodotInputHandler implements InputDeviceListener { // Check if the device has not been already added if (id < 0) { - InputDevice device = inputManager.getInputDevice(deviceId); + InputDevice device = mInputManager.getInputDevice(deviceId); //device can be null if deviceId is not found if (device != null) { int sources = device.getSources(); if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { - id = joysticksDevices.size(); + id = mJoysticksDevices.size(); Joystick joy = new Joystick(); joy.device_id = deviceId; @@ -249,7 +247,7 @@ public class GodotInputHandler implements InputDeviceListener { } } - joysticksDevices.add(joy); + mJoysticksDevices.add(joy); final int device_id = id; final String name = joy.name; @@ -270,7 +268,7 @@ public class GodotInputHandler implements InputDeviceListener { // Check if the evice has not been already removed if (device_id > -1) { - joysticksDevices.remove(device_id); + mJoysticksDevices.remove(device_id); queueEvent(new Runnable() { @Override @@ -360,8 +358,8 @@ public class GodotInputHandler implements InputDeviceListener { } private int findJoystickDevice(int device_id) { - for (int i = 0; i < joysticksDevices.size(); i++) { - if (joysticksDevices.get(i).device_id == device_id) { + for (int i = 0; i < mJoysticksDevices.size(); i++) { + if (mJoysticksDevices.get(i).device_id == device_id) { return i; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index 18f2d57661..9cd08de529 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -29,6 +29,9 @@ /*************************************************************************/ package org.godotengine.godot.input; + +import org.godotengine.godot.*; + import android.content.Context; import android.text.Editable; import android.text.TextWatcher; @@ -37,7 +40,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -import org.godotengine.godot.*; public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListener { // =========================================================== @@ -48,7 +50,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // =========================================================== // Fields // =========================================================== - private final GodotView mView; + private final GodotRenderView mRenderView; private final GodotEditText mEdit; private String mOriginText; @@ -56,9 +58,9 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // Constructors // =========================================================== - public GodotTextInputWrapper(final GodotView view, final GodotEditText edit) { - this.mView = view; - this.mEdit = edit; + public GodotTextInputWrapper(final GodotRenderView view, final GodotEditText edit) { + mRenderView = view; + mEdit = edit; } // =========================================================== @@ -66,13 +68,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // =========================================================== private boolean isFullScreenEdit() { - final TextView textField = this.mEdit; + final TextView textField = mEdit; final InputMethodManager imm = (InputMethodManager)textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return imm.isFullscreenMode(); } public void setOriginText(final String originText) { - this.mOriginText = originText; + mOriginText = originText; } // =========================================================== @@ -87,7 +89,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) { //Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < count; ++i) { @@ -106,7 +108,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for (int i = start; i < start + count; ++i) { newChars[i - start] = pCharSequence.charAt(i); } - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < count; ++i) { @@ -124,10 +126,10 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) { - if (this.mEdit == pTextView && this.isFullScreenEdit()) { + if (mEdit == pTextView && isFullScreenEdit()) { final String characters = pKeyEvent.getCharacters(); - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < characters.length(); i++) { @@ -144,7 +146,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false); - this.mView.requestFocus(); + mRenderView.getView().requestFocus(); return true; } return false; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java index 4042c42e9d..62810ad3a4 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java @@ -120,7 +120,6 @@ public interface InputManagerCompat { * Use this to construct a compatible InputManager. */ public static class Factory { - /** * Constructs and returns a compatible InputManger * diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java index e4bafa7ff9..61828dccae 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java @@ -23,12 +23,12 @@ import android.os.Build; import android.os.Handler; import android.view.InputDevice; import android.view.MotionEvent; + import java.util.HashMap; import java.util.Map; @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class InputManagerV16 implements InputManagerCompat { - private final InputManager mInputManager; private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java index 0c1bdb32aa..1f3fe1e527 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java @@ -31,6 +31,7 @@ package org.godotengine.godot.input; import android.view.InputDevice.MotionRange; + import java.util.ArrayList; /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index e3683704e1..431bd4f5f9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -30,20 +30,27 @@ package org.godotengine.godot.plugin; +import org.godotengine.godot.BuildConfig; +import org.godotengine.godot.Godot; + import android.app.Activity; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.util.Log; import android.view.Surface; import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -import org.godotengine.godot.Godot; /** * Base class for the Godot Android plugins. @@ -69,8 +76,10 @@ import org.godotengine.godot.Godot; * 'godot/plugin/v1/[PluginName]/' */ public abstract class GodotPlugin { + private static final String TAG = GodotPlugin.class.getSimpleName(); private final Godot godot; + private final ConcurrentHashMap<String, SignalInfo> registeredSignals = new ConcurrentHashMap<>(); public GodotPlugin(Godot godot) { this.godot = godot; @@ -118,6 +127,13 @@ public abstract class GodotPlugin { nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt); } + // Register the signals for this plugin. + for (SignalInfo signalInfo : getPluginSignals()) { + String signalName = signalInfo.getName(); + nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames()); + registeredSignals.put(signalName, signalInfo); + } + // Get the list of gdnative libraries to register. Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths(); if (!gdnativeLibrariesPaths.isEmpty()) { @@ -220,7 +236,17 @@ public abstract class GodotPlugin { * Returns the list of methods to be exposed to Godot. */ @NonNull - public abstract List<String> getPluginMethods(); + public List<String> getPluginMethods() { + return Collections.emptyList(); + } + + /** + * Returns the list of signals to be exposed to Godot. + */ + @NonNull + public Set<SignalInfo> getPluginSignals() { + return Collections.emptySet(); + } /** * Returns the paths for the plugin's gdnative libraries. @@ -253,6 +279,49 @@ public abstract class GodotPlugin { } /** + * Emit a registered Godot signal. + * @param signalName + * @param signalArgs + */ + protected void emitSignal(final String signalName, final Object... signalArgs) { + try { + // Check that the given signal is among the registered set. + SignalInfo signalInfo = registeredSignals.get(signalName); + if (signalInfo == null) { + throw new IllegalArgumentException( + "Signal " + signalName + " is not registered for this plugin."); + } + + // Validate the arguments count. + Class<?>[] signalParamTypes = signalInfo.getParamTypes(); + if (signalArgs.length != signalParamTypes.length) { + throw new IllegalArgumentException( + "Invalid arguments count. Should be " + signalParamTypes.length + " but is " + signalArgs.length); + } + + // Validate the argument's types. + for (int i = 0; i < signalParamTypes.length; i++) { + if (!signalParamTypes[i].isInstance(signalArgs[i])) { + throw new IllegalArgumentException( + "Invalid type for argument #" + i + ". Should be of type " + signalParamTypes[i].getName()); + } + } + + runOnRenderThread(new Runnable() { + @Override + public void run() { + nativeEmitSignal(getPluginName(), signalName, signalArgs); + } + }); + } catch (IllegalArgumentException exception) { + Log.w(TAG, exception.getMessage()); + if (BuildConfig.DEBUG) { + throw exception; + } + } + } + + /** * Used to setup a {@link GodotPlugin} instance. * @param p_name Name of the instance. */ @@ -272,4 +341,20 @@ public abstract class GodotPlugin { * @param gdnlibPaths Paths to the libraries relative to the 'assets' directory. */ private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths); + + /** + * Used to complete registration of the {@link GodotPlugin} instance's methods. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to register + * @param signalParamTypes Signal parameters types + */ + private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes); + + /** + * Used to emit signal by {@link GodotPlugin} instance. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to emit + * @param signalParams Signal parameters + */ + private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java index e13a9c15d8..12d2ed09fb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java @@ -30,26 +30,27 @@ package org.godotengine.godot.plugin; +import org.godotengine.godot.Godot; + import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; + +import androidx.annotation.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.godotengine.godot.Godot; /** * Registry used to load and access the registered Godot Android plugins. */ public final class GodotPluginRegistry { - private static final String TAG = GodotPluginRegistry.class.getSimpleName(); private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1."; @@ -57,7 +58,9 @@ public final class GodotPluginRegistry { /** * Name for the metadata containing the list of Godot plugins to enable. */ - private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins"; + private static final String GODOT_ENABLED_PLUGINS_LABEL = "plugins"; + + private static final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|"; private static GodotPluginRegistry instance; private final ConcurrentHashMap<String, GodotPlugin> registry; @@ -127,13 +130,13 @@ public final class GodotPluginRegistry { } // When using the Godot editor for building and exporting the apk, this is used to check - // which plugins to enable since the custom build template may contain prebuilt plugins. + // which plugins to enable. // When using a custom process to generate the apk, the metadata is not needed since // it's assumed that the developer is aware of the dependencies included in the apk. final Set<String> enabledPluginsSet; if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) { String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, ""); - String[] enabledPluginsList = enabledPlugins.split(","); + String[] enabledPluginsList = enabledPlugins.split(PLUGIN_VALUE_SEPARATOR_REGEX); if (enabledPluginsList.length == 0) { // No plugins to enable. Aborting early. return; @@ -157,6 +160,8 @@ public final class GodotPluginRegistry { continue; } + Log.i(TAG, "Initializing Godot plugin " + pluginName); + // Retrieve the plugin class full name. String pluginHandleClassFullName = metaData.getString(metaDataName); if (!TextUtils.isEmpty(pluginHandleClassFullName)) { @@ -176,6 +181,7 @@ public final class GodotPluginRegistry { "Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName()); } registry.put(pluginName, pluginHandle); + Log.i(TAG, "Completed initialization for Godot plugin " + pluginHandle.getPluginName()); } catch (ClassNotFoundException e) { Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); } catch (IllegalAccessException e) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java new file mode 100644 index 0000000000..f82c4d3fa0 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* SignalInfo.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.util.Arrays; + +/** + * Store information about a {@link GodotPlugin}'s signal. + */ +public final class SignalInfo { + private final String name; + private final Class<?>[] paramTypes; + private final String[] paramTypesNames; + + public SignalInfo(@NonNull String signalName, Class<?>... paramTypes) { + if (TextUtils.isEmpty(signalName)) { + throw new IllegalArgumentException("Invalid signal name: " + signalName); + } + + this.name = signalName; + this.paramTypes = paramTypes == null ? new Class<?>[ 0 ] : paramTypes; + this.paramTypesNames = new String[this.paramTypes.length]; + for (int i = 0; i < this.paramTypes.length; i++) { + this.paramTypesNames[i] = this.paramTypes[i].getName(); + } + } + + public String getName() { + return name; + } + + Class<?>[] getParamTypes() { + return paramTypes; + } + + String[] getParamTypesNames() { + return paramTypesNames; + } + + @Override + public String toString() { + return "SignalInfo{" + + + "name='" + name + '\'' + + ", paramsTypes=" + Arrays.toString(paramTypes) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SignalInfo)) { + return false; + } + + SignalInfo that = (SignalInfo)o; + + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java index bc0e565774..acc9c4981b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java @@ -34,7 +34,6 @@ import java.security.MessageDigest; import java.util.Random; public class Crypt { - public static String md5(String input) { try { // Create MD5 Hash diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java index 9d29551f89..82420eda79 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java @@ -31,6 +31,7 @@ package org.godotengine.godot.utils; import android.util.Log; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; @@ -39,7 +40,6 @@ import javax.microedition.khronos.egl.EGLDisplay; * Contains GL utilities methods. */ public class GLUtils { - private static final String TAG = GLUtils.class.getSimpleName(); public static final boolean DEBUG = false; diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java index 011d426c7e..0832a9b965 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java @@ -30,10 +30,11 @@ package org.godotengine.godot.utils; +import org.godotengine.godot.Godot; + import android.content.Context; import android.net.wifi.WifiManager; import android.util.Log; -import org.godotengine.godot.Godot; /** * This class handles Android-specific networking functions. @@ -41,7 +42,6 @@ import org.godotengine.godot.Godot; * to receive broadcast and multicast packets. */ public class GodotNetUtils { - /* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */ private WifiManager.MulticastLock multicastLock; diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java index 7cf32b00fe..6837e4f147 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -30,21 +30,26 @@ package org.godotengine.godot.utils; +import org.godotengine.godot.Godot; + import android.Manifest; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.os.Build; -import android.support.v4.content.ContextCompat; +import android.util.Log; + +import androidx.core.content.ContextCompat; + import java.util.ArrayList; import java.util.List; -import org.godotengine.godot.Godot; /** * This class includes utility functions for Android permissions related operations. * @author Cagdas Caglak <cagdascaglak@gmail.com> */ public final class PermissionsUtil { + private static final String TAG = PermissionsUtil.class.getSimpleName(); static final int REQUEST_RECORD_AUDIO_PERMISSION = 1; static final int REQUEST_CAMERA_PERMISSION = 2; @@ -113,8 +118,8 @@ public final class PermissionsUtil { dangerousPermissions.add(manifestPermission); } } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return false; + // Skip this permission and continue. + Log.w(TAG, "Unable to identify permission " + manifestPermission, e); } } @@ -153,8 +158,8 @@ public final class PermissionsUtil { dangerousPermissions.add(manifestPermission); } } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return new String[0]; + // Skip this permission and continue. + Log.w(TAG, "Unable to identify permission " + manifestPermission, e); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt index 67faad8ddd..608ad48df9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt @@ -33,6 +33,11 @@ package org.godotengine.godot.vulkan import android.view.Surface +import org.godotengine.godot.Godot +import org.godotengine.godot.GodotLib +import org.godotengine.godot.plugin.GodotPlugin +import org.godotengine.godot.plugin.GodotPluginRegistry + /** * Responsible to setting up and driving the Vulkan rendering logic. * @@ -48,52 +53,64 @@ import android.view.Surface */ internal class VkRenderer { + private val pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry() + /** * Called when the surface is created and signals the beginning of rendering. */ fun onVkSurfaceCreated(surface: Surface) { - nativeOnVkSurfaceCreated(surface) + // TODO: properly implement surface re-creation: + // GodotLib.newcontext should be called here once it's done. + //GodotLib.newcontext(surface, false) + + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkSurfaceCreated(surface) + } } /** * Called after the surface is created and whenever its size changes. */ fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) { - nativeOnVkSurfaceChanged(surface, width, height) + GodotLib.resize(width, height) + + // TODO: properly implement surface re-creation: + // Update the native renderer instead of restarting the app. + // GodotLib.newcontext should not be called here once it's done. + GodotLib.newcontext(surface, false) + + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkSurfaceChanged(surface, width, height) + } } /** * Called to draw the current frame. */ fun onVkDrawFrame() { - nativeOnVkDrawFrame() + GodotLib.step() + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkDrawFrame() + } } /** * Called when the rendering thread is resumed. */ fun onVkResume() { - nativeOnVkResume() + GodotLib.onRendererResumed() } /** * Called when the rendering thread is paused. */ fun onVkPause() { - nativeOnVkPause() + GodotLib.onRendererPaused() } /** * Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic. */ fun onVkDestroy() { - nativeOnVkDestroy() } - - private external fun nativeOnVkSurfaceCreated(surface: Surface) - private external fun nativeOnVkSurfaceChanged(surface: Surface, width: Int, height: Int) - private external fun nativeOnVkResume() - private external fun nativeOnVkDrawFrame() - private external fun nativeOnVkPause() - private external fun nativeOnVkDestroy() } diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt index 1c594f3201..6b0e12b21a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt @@ -49,7 +49,7 @@ import android.view.SurfaceView * UI thread. * </ul> */ -internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback { +open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback { companion object { fun checkState(expression: Boolean, errorMessage: Any) { @@ -100,7 +100,7 @@ internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHo * * Must not be called before a [VkRenderer] has been set. */ - fun onResume() { + open fun onResume() { vkThread.onResume() } @@ -109,7 +109,7 @@ internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHo * * Must not be called before a [VkRenderer] has been set. */ - fun onPause() { + open fun onPause() { vkThread.onPause() } diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt index 2e332840bf..7557c8aa22 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt @@ -219,9 +219,9 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk vkRenderer.onVkDrawFrame() } } catch (ex: InterruptedException) { - Log.i(TAG, ex.message) + Log.i(TAG, "InterruptedException", ex) } catch (ex: IllegalStateException) { - Log.i(TAG, ex.message) + Log.i(TAG, "IllegalStateException", ex) } finally { threadExiting() } diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java index 9209d6ccf2..819bcccdf1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java @@ -32,6 +32,7 @@ package org.godotengine.godot.xr.ovr; import android.opengl.EGLExt; import android.opengl.GLSurfaceView; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; @@ -40,7 +41,6 @@ import javax.microedition.khronos.egl.EGLDisplay; * EGL config chooser for the Oculus Mobile VR SDK. */ public class OvrConfigChooser implements GLSurfaceView.EGLConfigChooser { - private static final int[] CONFIG_ATTRIBS = { EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java index 36f4416df2..2d9b921466 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java @@ -32,6 +32,7 @@ package org.godotengine.godot.xr.ovr; import android.opengl.EGL14; import android.opengl.GLSurfaceView; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; @@ -41,7 +42,6 @@ import javax.microedition.khronos.egl.EGLDisplay; * EGL Context factory for the Oculus mobile VR SDK. */ public class OvrContextFactory implements GLSurfaceView.EGLContextFactory { - private static final int[] CONTEXT_ATTRIBS = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java index b2aa130f37..43c7f0f966 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java @@ -31,6 +31,7 @@ package org.godotengine.godot.xr.ovr; import android.opengl.GLSurfaceView; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; @@ -40,7 +41,6 @@ import javax.microedition.khronos.egl.EGLSurface; * EGL window surface factory for the Oculus mobile VR SDK. */ public class OvrWindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory { - private final static int[] SURFACE_ATTRIBS = { EGL10.EGL_WIDTH, 16, EGL10.EGL_HEIGHT, 16, diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java index 8409e37f8f..54672db282 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java @@ -30,17 +30,18 @@ package org.godotengine.godot.xr.regular; +import org.godotengine.godot.utils.GLUtils; + import android.opengl.GLSurfaceView; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; -import org.godotengine.godot.utils.GLUtils; /** * Used to select the egl config for pancake games. */ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { - private static final String TAG = RegularConfigChooser.class.getSimpleName(); private int[] mValue = new int[1]; @@ -72,7 +73,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { } public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - /* Get the number of minimally matching EGL configurations */ int[] num_config = new int[1]; @@ -127,7 +127,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) { - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { return mValue[0]; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java index f2b4c95a2c..126f3ad5f5 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -30,14 +30,16 @@ package org.godotengine.godot.xr.regular; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.utils.GLUtils; + import android.opengl.GLSurfaceView; import android.util.Log; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.utils.GLUtils; /** * Factory used to setup the opengl context for pancake games. @@ -51,7 +53,6 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name"); // FIXME: Add support for Vulkan. Log.w(TAG, "creating OpenGL ES 2.0 context :"); diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java index 71fcf06020..c83c47bed7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java @@ -30,15 +30,16 @@ package org.godotengine.godot.xr.regular; +import org.godotengine.godot.utils.GLUtils; + import android.util.Log; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; -import org.godotengine.godot.utils.GLUtils; /* Fallback if 32bit View is not supported*/ public class RegularFallbackConfigChooser extends RegularConfigChooser { - private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName(); private RegularConfigChooser fallback; diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle index 4f376c4587..ffab86e26e 100644 --- a/platform/android/java/plugins/godotpayment/build.gradle +++ b/platform/android/java/plugins/godotpayment/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools + useLibrary 'org.apache.http.legacy' defaultConfig { minSdkVersion versions.minSdk diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java index c15bc232ce..de009f6d16 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java @@ -33,11 +33,12 @@ package org.godotengine.godot.plugin.payment; import android.content.Context; import android.os.AsyncTask; import android.os.RemoteException; + import com.android.vending.billing.IInAppBillingService; + import java.lang.ref.WeakReference; abstract public class ConsumeTask { - private Context context; private IInAppBillingService mService; @@ -45,7 +46,6 @@ abstract public class ConsumeTask { private String mToken; private static class ConsumeAsyncTask extends AsyncTask<String, String, String> { - private WeakReference<ConsumeTask> mTask; ConsumeAsyncTask(ConsumeTask consume) { diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java index c7d0a5de65..ded7f0a9aa 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java @@ -30,21 +30,24 @@ package org.godotengine.godot.plugin.payment; +import org.godotengine.godot.Dictionary; +import org.godotengine.godot.Godot; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.plugin.GodotPlugin; + import android.content.Intent; -import android.support.annotation.NonNull; import android.util.Log; + +import androidx.annotation.NonNull; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.godotengine.godot.Dictionary; -import org.godotengine.godot.Godot; -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.plugin.GodotPlugin; + import org.json.JSONException; import org.json.JSONObject; public class GodotPayment extends GodotPlugin { - private Integer purchaseCallbackId = 0; private String accessToken; private String purchaseValidationUrlPrefix; diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java index fe5685288b..00e216e8c0 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java @@ -32,11 +32,11 @@ package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.content.Intent; + import org.json.JSONException; import org.json.JSONObject; abstract public class HandlePurchaseTask { - private Activity context; public HandlePurchaseTask(Activity context) { diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java index d5919e3d9d..435f43c49d 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.SharedPreferences; public class PaymentsCache { - public Context context; public PaymentsCache(Context context) { diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java index bded1f452f..9b3a338866 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java @@ -40,14 +40,16 @@ import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; + import com.android.vending.billing.IInAppBillingService; + import java.util.ArrayList; import java.util.Arrays; + import org.json.JSONException; import org.json.JSONObject; public class PaymentsManager { - public static final int BILLING_RESPONSE_RESULT_OK = 0; public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; private static boolean auto_consume = true; @@ -155,7 +157,6 @@ public class PaymentsManager { Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken); if (bundle.getInt("RESPONSE_CODE") == 0) { - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); @@ -165,7 +166,6 @@ public class PaymentsManager { } for (int i = 0; i < myPurchases.size(); i++) { - try { String receipt = myPurchases.get(i); JSONObject inappPurchaseData = new JSONObject(receipt); @@ -226,11 +226,9 @@ public class PaymentsManager { } public void validatePurchase(String purchaseToken, final String sku) { - new ValidateTask(activity, godotPayment) { @Override protected void success() { - new ConsumeTask(mService, activity) { @Override protected void success(String ticket) { diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java index eecd1d2151..2742bb15bf 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java @@ -37,10 +37,10 @@ import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; + import com.android.vending.billing.IInAppBillingService; abstract public class PurchaseTask { - private Activity context; private IInAppBillingService mService; diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java index b7bd638feb..1d52cf0fa5 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java @@ -34,19 +34,20 @@ import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; + import com.android.vending.billing.IInAppBillingService; + import java.lang.ref.WeakReference; import java.util.ArrayList; + import org.json.JSONException; import org.json.JSONObject; abstract public class ReleaseAllConsumablesTask { - private Context context; private IInAppBillingService mService; private static class ReleaseAllConsumablesAsyncTask extends AsyncTask<String, String, String> { - private WeakReference<ReleaseAllConsumablesTask> mTask; private String mSku; private String mReceipt; @@ -91,7 +92,6 @@ abstract public class ReleaseAllConsumablesTask { Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp", null); if (bundle.getInt("RESPONSE_CODE") == 0) { - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); @@ -103,7 +103,6 @@ abstract public class ReleaseAllConsumablesTask { //Log.d("godot", "# products to be consumed:" + myPurchases.size()); for (int i = 0; i < myPurchases.size(); i++) { - try { String receipt = myPurchases.get(i); JSONObject inappPurchaseData = new JSONObject(receipt); diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java index 179cc08ed1..a7156152ce 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java @@ -30,17 +30,19 @@ package org.godotengine.godot.plugin.payment; +import org.godotengine.godot.plugin.payment.utils.HttpRequester; +import org.godotengine.godot.plugin.payment.utils.RequestParams; + import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; + import java.lang.ref.WeakReference; -import org.godotengine.godot.utils.HttpRequester; -import org.godotengine.godot.utils.RequestParams; + import org.json.JSONException; import org.json.JSONObject; abstract public class ValidateTask { - private Activity context; private GodotPayment godotPayments; private ProgressDialog dialog; diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java index c78e8c1c66..55b87b49e5 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java @@ -28,7 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; + import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; @@ -37,8 +38,10 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; + import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; + import org.apache.http.conn.ssl.SSLSocketFactory; /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java index 68f9a83597..0afcf60f38 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java @@ -28,11 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; + +import org.godotengine.godot.utils.Crypt; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -40,6 +43,7 @@ import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.security.KeyStore; import java.util.Date; + import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.client.ClientProtocolException; @@ -67,7 +71,6 @@ import org.apache.http.util.EntityUtils; * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class HttpRequester { - private Context context; private static final int TTL = 600000; // 10 minutos private long cttl = 0; diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java index 25fa10647d..6b66c7e474 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; + import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; @@ -42,7 +42,6 @@ import org.apache.http.message.BasicNameValuePair; * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class RequestParams { - private HashMap<String, String> params; private String url; diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 6b9105b8e5..39de3cb642 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -33,7 +33,6 @@ #include "thread_jandroid.h" bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) { - Map<StringName, List<MethodInfo>>::Element *M = methods.find(p_method); if (!M) return false; @@ -42,7 +41,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, MethodInfo *method = nullptr; for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) { - if (!p_instance && !E->get()._static) { r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; continue; @@ -50,13 +48,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, int pc = E->get().param_types.size(); if (pc > p_argcount) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = pc; continue; } if (pc < p_argcount) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_error.argument = pc; continue; @@ -65,10 +61,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, bool valid = true; for (int i = 0; i < pc; i++) { - Variant::Type arg_expected = Variant::NIL; switch (ptypes[i]) { - case ARG_TYPE_VOID: { //bug? } break; @@ -86,7 +80,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, case ARG_TYPE_SHORT: case ARG_TYPE_INT: case ARG_TYPE_LONG: { - if (!p_args[i]->is_num()) arg_expected = Variant::INT; @@ -95,32 +88,26 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: case ARG_TYPE_FLOAT: case ARG_TYPE_DOUBLE: { - if (!p_args[i]->is_num()) arg_expected = Variant::FLOAT; } break; case ARG_TYPE_STRING: { - if (p_args[i]->get_type() != Variant::STRING) arg_expected = Variant::STRING; } break; case ARG_TYPE_CLASS: { - if (p_args[i]->get_type() != Variant::OBJECT) arg_expected = Variant::OBJECT; else { - Ref<Reference> ref = *p_args[i]; if (!ref.is_null()) { if (Object::cast_to<JavaObject>(ref.ptr())) { - Ref<JavaObject> jo = ref; //could be faster jclass c = env->FindClass(E->get().param_sigs[i].operator String().utf8().get_data()); if (!c || !env->IsInstanceOf(jo->instance, c)) { - arg_expected = Variant::OBJECT; } else { //ok @@ -133,7 +120,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; default: { - if (p_args[i]->get_type() != Variant::ARRAY) arg_expected = Variant::ARRAY; @@ -163,13 +149,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, jvalue *argv = nullptr; if (method->param_types.size()) { - argv = (jvalue *)alloca(sizeof(jvalue) * method->param_types.size()); } List<jobject> to_free; for (int i = 0; i < method->param_types.size(); i++) { - switch (method->param_types[i]) { case ARG_TYPE_VOID: { //can't happen @@ -279,10 +263,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(jStr); } break; case ARG_TYPE_CLASS: { - Ref<JavaObject> jo = *p_args[i]; if (jo.is_valid()) { - argv[i].l = jo->instance; } else { argv[i].l = nullptr; //I hope this works @@ -290,7 +272,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: { - Array arr = *p_args[i]; jbooleanArray a = env->NewBooleanArray(arr.size()); for (int j = 0; j < arr.size(); j++) { @@ -302,7 +283,6 @@ 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++) { @@ -314,7 +294,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } 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++) { @@ -326,7 +305,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } 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++) { @@ -338,7 +316,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } 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++) { @@ -360,7 +337,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } 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++) { @@ -372,7 +348,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } 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++) { @@ -384,11 +359,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_ARRAY_BIT | ARG_TYPE_STRING: { - 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); @@ -399,7 +372,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(a); } break; case ARG_ARRAY_BIT | ARG_TYPE_CLASS: { - argv[i].l = nullptr; } break; } @@ -409,7 +381,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, bool success = true; switch (method->return_type) { - case ARG_TYPE_VOID: { if (method->_static) { env->CallStaticVoidMethodA(_class, method->method, argv); @@ -434,7 +405,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } } break; case ARG_TYPE_CHAR: { - if (method->_static) { ret = env->CallStaticCharMethodA(_class, method->method, argv); } else { @@ -442,7 +412,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } } break; case ARG_TYPE_SHORT: { - if (method->_static) { ret = env->CallStaticShortMethodA(_class, method->method, argv); } else { @@ -451,7 +420,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_TYPE_INT: { - if (method->_static) { ret = env->CallStaticIntMethodA(_class, method->method, argv); } else { @@ -460,7 +428,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_TYPE_LONG: { - if (method->_static) { ret = (int64_t)env->CallStaticLongMethodA(_class, method->method, argv); } else { @@ -469,7 +436,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_TYPE_FLOAT: { - if (method->_static) { ret = env->CallStaticFloatMethodA(_class, method->method, argv); } else { @@ -478,7 +444,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_TYPE_DOUBLE: { - if (method->_static) { ret = env->CallStaticDoubleMethodA(_class, method->method, argv); } else { @@ -487,7 +452,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; default: { - jobject obj; if (method->_static) { obj = env->CallStaticObjectMethodA(_class, method->method, argv); @@ -498,7 +462,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (!obj) { ret = Variant(); } else { - if (!_convert_object_to_variant(env, obj, ret, method->return_type)) { ret = Variant(); r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; @@ -518,7 +481,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } Variant JavaClass::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - Variant ret; bool found = _call_method(nullptr, p_method, p_args, p_argcount, r_error, ret); if (found) { @@ -534,7 +496,6 @@ JavaClass::JavaClass() { ///////////////////// Variant JavaObject::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - return Variant(); } @@ -547,14 +508,12 @@ JavaObject::~JavaObject() { //////////////////// bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig) { - jstring name2 = (jstring)env->CallObjectMethod(obj, Class_getName); String str_type = jstring_to_string(name2, env); env->DeleteLocalRef(name2); uint32_t t = 0; if (str_type.begins_with("[")) { - t = JavaClass::ARG_ARRAY_BIT; strsig = "["; str_type = str_type.substr(1, str_type.length() - 1); @@ -633,87 +592,71 @@ bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, St } bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &var, uint32_t p_sig) { - if (!obj) { var = Variant(); //seems null is just null... return true; } switch (p_sig) { - case ARG_TYPE_VOID: { - return Variant(); } break; case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT: { - var = env->CallBooleanMethod(obj, JavaClassWrapper::singleton->Boolean_booleanValue); return true; } break; case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT: { - var = env->CallByteMethod(obj, JavaClassWrapper::singleton->Byte_byteValue); return true; } break; case ARG_TYPE_CHAR | ARG_NUMBER_CLASS_BIT: { - var = env->CallCharMethod(obj, JavaClassWrapper::singleton->Character_characterValue); return true; } break; case ARG_TYPE_SHORT | ARG_NUMBER_CLASS_BIT: { - var = env->CallShortMethod(obj, JavaClassWrapper::singleton->Short_shortValue); return true; } break; case ARG_TYPE_INT | ARG_NUMBER_CLASS_BIT: { - var = env->CallIntMethod(obj, JavaClassWrapper::singleton->Integer_integerValue); return true; } break; case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT: { - var = (int64_t)env->CallLongMethod(obj, JavaClassWrapper::singleton->Long_longValue); return true; } break; case ARG_TYPE_FLOAT | ARG_NUMBER_CLASS_BIT: { - var = env->CallFloatMethod(obj, JavaClassWrapper::singleton->Float_floatValue); return true; } break; case ARG_TYPE_DOUBLE | ARG_NUMBER_CLASS_BIT: { - var = env->CallDoubleMethod(obj, JavaClassWrapper::singleton->Double_doubleValue); return true; } break; case ARG_TYPE_STRING: { - var = jstring_to_string((jstring)obj, env); return true; } break; case ARG_TYPE_CLASS: { - return false; } break; case ARG_ARRAY_BIT | ARG_TYPE_VOID: { - var = Array(); // ? return true; } break; case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: { - Array ret; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jboolean val; env->GetBooleanArrayRegion((jbooleanArray)arr, 0, 1, &val); ret.push_back(val); @@ -724,14 +667,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va } break; case ARG_ARRAY_BIT | ARG_TYPE_BYTE: { - Array 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); @@ -747,7 +688,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va 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); @@ -763,7 +703,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jshort val; env->GetShortArrayRegion((jshortArray)arr, 0, 1, &val); ret.push_back(val); @@ -779,7 +718,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va 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); @@ -795,7 +733,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va 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); @@ -811,7 +748,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va 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); @@ -827,7 +763,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va 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); @@ -837,14 +772,12 @@ 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_BOOLEAN: { - Array ret; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -860,14 +793,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -882,14 +813,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -904,14 +833,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -926,14 +853,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -948,14 +873,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -970,14 +893,12 @@ 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; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -998,7 +919,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -1014,14 +934,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va } break; case ARG_ARRAY_BIT | ARG_TYPE_STRING: { - Array ret; jobjectArray arr = (jobjectArray)obj; int count = env->GetArrayLength(arr); for (int i = 0; i < count; i++) { - jobject o = env->GetObjectArrayElement(arr, i); if (!o) ret.push_back(Variant()); @@ -1036,7 +954,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va return true; } break; case ARG_ARRAY_BIT | ARG_TYPE_CLASS: { - } break; } @@ -1044,7 +961,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va } Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { - if (class_cache.has(p_class)) return class_cache[p_class]; @@ -1066,7 +982,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { int count = env->GetArrayLength(methods); for (int i = 0; i < count; i++) { - jobject obj = env->GetObjectArrayElement(methods, i); ERR_CONTINUE(!obj); @@ -1096,7 +1011,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { String signature = "("; for (int j = 0; j < count2; j++) { - jobject obj2 = env->GetObjectArrayElement(param_types, j); String strsig; uint32_t sig = 0; @@ -1138,7 +1052,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { bool discard = false; for (List<JavaClass::MethodInfo>::Element *E = java_class->methods[str_method].front(); E; E = E->next()) { - float new_likeliness = 0; float existing_likeliness = 0; @@ -1146,7 +1059,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { continue; bool valid = true; for (int j = 0; j < E->get().param_types.size(); j++) { - Variant::Type _new; float new_l; Variant::Type existing; @@ -1195,7 +1107,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { count = env->GetArrayLength(fields); for (int i = 0; i < count; i++) { - jobject obj = env->GetObjectArrayElement(fields, i); ERR_CONTINUE(!obj); @@ -1207,17 +1118,13 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { jobject objc = env->CallObjectMethod(obj, Field_get, nullptr); if (objc) { - uint32_t sig; String strsig; jclass cl = env->GetObjectClass(objc); if (JavaClassWrapper::_get_type_sig(env, cl, sig, strsig)) { - if ((sig & JavaClass::ARG_TYPE_MASK) <= JavaClass::ARG_TYPE_STRING) { - Variant value; if (JavaClass::_convert_object_to_variant(env, objc, value, sig)) { - java_class->constant_map[str_field] = value; } } @@ -1239,7 +1146,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { JavaClassWrapper *JavaClassWrapper::singleton = nullptr; JavaClassWrapper::JavaClassWrapper(jobject p_activity) { - singleton = this; JNIEnv *env = ThreadAndroid::get_env(); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 8d075f8e97..0da0bd6387 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -56,11 +56,8 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;I)V"); _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); + _get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I"); _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;"); - _play_video = p_env->GetMethodID(cls, "playVideo", "(Ljava/lang/String;)V"); - _is_video_playing = p_env->GetMethodID(cls, "isVideoPlaying", "()Z"); - _pause_video = p_env->GetMethodID(cls, "pauseVideo", "()V"); - _stop_video = p_env->GetMethodID(cls, "stopVideo", "()V"); } } @@ -157,40 +154,22 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { } } -String GodotIOJavaWrapper::get_system_dir(int p_dir) { - if (_get_system_dir) { +int GodotIOJavaWrapper::get_screen_orientation() { + if (_get_screen_orientation) { JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir); - return jstring_to_string(s, env); + return env->CallIntMethod(godot_io_instance, _get_screen_orientation); } else { - return String("."); + return 0; } } -void GodotIOJavaWrapper::play_video(const String &p_path) { - // Why is this not here?!?! -} - -bool GodotIOJavaWrapper::is_video_playing() { - if (_is_video_playing) { +String GodotIOJavaWrapper::get_system_dir(int p_dir) { + if (_get_system_dir) { JNIEnv *env = ThreadAndroid::get_env(); - return env->CallBooleanMethod(godot_io_instance, _is_video_playing); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir); + return jstring_to_string(s, env); } else { - return false; - } -} - -void GodotIOJavaWrapper::pause_video() { - if (_pause_video) { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io_instance, _pause_video); - } -} - -void GodotIOJavaWrapper::stop_video() { - if (_stop_video) { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io_instance, _stop_video); + return String("."); } } diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index 7dfed52187..dbb3b564f6 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -54,11 +54,8 @@ private: jmethodID _show_keyboard = 0; jmethodID _hide_keyboard = 0; jmethodID _set_screen_orientation = 0; + jmethodID _get_screen_orientation = 0; jmethodID _get_system_dir = 0; - jmethodID _play_video = 0; - jmethodID _is_video_playing = 0; - jmethodID _pause_video = 0; - jmethodID _stop_video = 0; public: GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance); @@ -78,11 +75,8 @@ public: int get_vk_height(); void set_vk_height(int p_height); void set_screen_orientation(int p_orient); + int get_screen_orientation(); String get_system_dir(int p_dir); - void play_video(const String &p_path); - bool is_video_playing(); - void pause_video(); - void stop_video(); }; #endif /* !JAVA_GODOT_IO_WRAPPER_H */ diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 9d44ab4619..23e0d24b57 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -34,13 +34,14 @@ #include "java_godot_wrapper.h" #include "android/asset_manager_jni.h" -#include "android_keys_utils.h" #include "api/java_class_wrapper.h" +#include "api/jni_singleton.h" #include "audio_driver_jandroid.h" #include "core/engine.h" -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "core/project_settings.h" #include "dir_access_jandroid.h" +#include "display_server_android.h" #include "file_access_android.h" #include "file_access_jandroid.h" #include "jni_utils.h" @@ -52,6 +53,8 @@ #include <unistd.h> +#include <android/native_window_jni.h> + static JavaClassWrapper *java_class_wrapper = nullptr; static OS_Android *os_android = nullptr; static GodotJavaWrapper *godot_java = nullptr; @@ -75,7 +78,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) { - initialized = true; JavaVM *jvm; @@ -134,7 +136,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc j_cmdline = (jstring *)malloc(cmdlen * sizeof(jstring)); for (int i = 0; i < cmdlen; i++) { - jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); const char *rawString = env->GetStringUTFChars(string, 0); @@ -160,22 +161,25 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc } java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity())); + ClassDB::register_class<JNISingleton>(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height) { - if (os_android) - os_android->set_display_size(Size2(width, height)); + os_android->set_display_size(Size2i(width, height)); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits) { - +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) { if (os_android) { if (step == 0) { // During startup os_android->set_context_is_16_bits(!p_32_bits); + if (p_surface) { + ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface); + os_android->set_native_window(native_window); + } } else { - // GL context recreated because it was lost; restart app to let it reload everything + // Rendering context recreated because it was lost; restart app to let it reload everything os_android->main_loop_end(); godot_java->restart(env); step = -1; // Ensure no further steps are attempted @@ -195,7 +199,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl return; if (step == 0) { - // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, // but for Godot purposes, the main thread is the one running the game loop Main::setup2(Thread::get_caller_id()); @@ -213,34 +216,31 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl ++step; } - os_android->process_accelerometer(accelerometer); - os_android->process_gravity(gravity); - os_android->process_magnetometer(magnetometer); - os_android->process_gyroscope(gyroscope); + DisplayServerAndroid::get_singleton()->process_accelerometer(accelerometer); + DisplayServerAndroid::get_singleton()->process_gravity(gravity); + DisplayServerAndroid::get_singleton()->process_magnetometer(magnetometer); + DisplayServerAndroid::get_singleton()->process_gyroscope(gyroscope); if (os_android->main_loop_iterate()) { - godot_java->force_quit(env); } } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) { - if (step == 0) return; - Vector<OS_Android::TouchPos> points; + Vector<DisplayServerAndroid::TouchPos> points; for (int i = 0; i < count; i++) { - jint p[3]; env->GetIntArrayRegion(positions, i * 3, 3, p); - OS_Android::TouchPos tp; + DisplayServerAndroid::TouchPos tp; tp.pos = Point2(p[1], p[2]); tp.id = p[0]; points.push_back(tp); } - os_android->process_touch(ev, pointer, points); + DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points); /* if (os_android) @@ -252,78 +252,78 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jc if (step == 0) return; - os_android->process_hover(p_type, Point2(p_x, p_y)); + DisplayServerAndroid::get_singleton()->process_hover(p_type, Point2(p_x, p_y)); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y) { if (step == 0) return; - os_android->process_double_tap(Point2(p_x, p_y)); + DisplayServerAndroid::get_singleton()->process_double_tap(Point2(p_x, p_y)); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) { if (step == 0) return; - os_android->process_scroll(Point2(p_x, p_y)); + DisplayServerAndroid::get_singleton()->process_scroll(Point2(p_x, p_y)); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) { if (step == 0) return; - OS_Android::JoypadEvent jevent; + DisplayServerAndroid::JoypadEvent jevent; jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_BUTTON; + jevent.type = DisplayServerAndroid::JOY_EVENT_BUTTON; jevent.index = p_button; jevent.pressed = p_pressed; - os_android->process_joy_event(jevent); + DisplayServerAndroid::get_singleton()->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) { if (step == 0) return; - OS_Android::JoypadEvent jevent; + DisplayServerAndroid::JoypadEvent jevent; jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_AXIS; + jevent.type = DisplayServerAndroid::JOY_EVENT_AXIS; jevent.index = p_axis; jevent.value = p_value; - os_android->process_joy_event(jevent); + DisplayServerAndroid::get_singleton()->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) { if (step == 0) return; - OS_Android::JoypadEvent jevent; + DisplayServerAndroid::JoypadEvent jevent; jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_HAT; + jevent.type = DisplayServerAndroid::JOY_EVENT_HAT; int hat = 0; if (p_hat_x != 0) { if (p_hat_x < 0) - hat |= InputDefault::HAT_MASK_LEFT; + hat |= Input::HAT_MASK_LEFT; else - hat |= InputDefault::HAT_MASK_RIGHT; + hat |= Input::HAT_MASK_RIGHT; } if (p_hat_y != 0) { if (p_hat_y < 0) - hat |= InputDefault::HAT_MASK_UP; + hat |= Input::HAT_MASK_UP; else - hat |= InputDefault::HAT_MASK_DOWN; + hat |= Input::HAT_MASK_DOWN; } jevent.hat = hat; - os_android->process_joy_event(jevent); + DisplayServerAndroid::get_singleton()->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name) { if (os_android) { String name = jstring_to_string(p_name, env); - os_android->joy_connection_changed(p_device, p_connected, name); + Input::get_singleton()->joy_connection_changed(p_device, p_connected, name); } } @@ -331,29 +331,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jcla if (step == 0) return; - Ref<InputEventKey> ievent; - ievent.instance(); - int val = p_unicode_char; - int keycode = android_get_keysym(p_keycode); - int phy_keycode = android_get_keysym(p_scancode); - ievent->set_keycode(keycode); - ievent->set_physical_keycode(phy_keycode); - ievent->set_unicode(val); - ievent->set_pressed(p_pressed); - - if (val == '\n') { - ievent->set_keycode(KEY_ENTER); - } else if (val == 61448) { - ievent->set_keycode(KEY_BACKSPACE); - ievent->set_unicode(KEY_BACKSPACE); - } else if (val == 61453) { - ievent->set_keycode(KEY_ENTER); - ievent->set_unicode(KEY_ENTER); - } else if (p_keycode == 4) { - os_android->main_loop_request_go_back(); - } - - os_android->process_event(ievent); + DisplayServerAndroid::get_singleton()->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) { @@ -373,7 +351,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) { - if (step == 0) return; @@ -381,7 +358,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) { - if (step == 0) return; @@ -389,20 +365,17 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) { - ThreadAndroid::setup_thread(); AudioDriverAndroid::thread_func(env); } JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) { - String js = jstring_to_string(path, env); return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { - Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); ERR_FAIL_COND(!obj); @@ -415,7 +388,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en Variant *vlist = (Variant *)alloca(sizeof(Variant) * count); Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count); for (int i = 0; i < count; i++) { - jobject obj = env->GetObjectArrayElement(params, i); Variant v; if (obj) @@ -434,7 +406,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { - Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); ERR_FAIL_COND(!obj); @@ -447,7 +418,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * Variant args[VARIANT_ARG_MAX]; for (int i = 0; i < MIN(count, VARIANT_ARG_MAX); i++) { - jobject obj = env->GetObjectArrayElement(params, i); if (obj) args[i] = _jobject_to_variant(env, obj); diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index a7a5970440..221d701e2b 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -41,7 +41,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions); diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index ded79a668f..8e1ae53b78 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -31,13 +31,10 @@ #include "jni_utils.h" jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) { - jvalret v; switch (p_type) { - case Variant::BOOL: { - if (force_jobject) { jclass bclass = env->FindClass("java/lang/Boolean"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V"); @@ -52,9 +49,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a }; } break; case Variant::INT: { - if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Integer"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V"); jvalue val; @@ -69,9 +64,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a }; } break; case Variant::FLOAT: { - if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Double"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V"); jvalue val; @@ -86,19 +79,16 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a }; } break; case Variant::STRING: { - String s = *p_arg; jstring jStr = env->NewStringUTF(s.utf8().get_data()); v.val.l = jStr; v.obj = jStr; } break; case Variant::PACKED_STRING_ARRAY: { - Vector<String> sarray = *p_arg; jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); for (int j = 0; j < sarray.size(); j++) { - jstring str = env->NewStringUTF(sarray[j].utf8().get_data()); env->SetObjectArrayElement(arr, j, str); env->DeleteLocalRef(str); @@ -109,7 +99,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::DICTIONARY: { - Dictionary dict = *p_arg; jclass dclass = env->FindClass("org/godotengine/godot/Dictionary"); jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V"); @@ -152,7 +141,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::PACKED_INT32_ARRAY: { - Vector<int> array = *p_arg; jintArray arr = env->NewIntArray(array.size()); const int *r = array.ptr(); @@ -171,7 +159,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::PACKED_FLOAT32_ARRAY: { - Vector<float> array = *p_arg; jfloatArray arr = env->NewFloatArray(array.size()); const float *r = array.ptr(); @@ -185,7 +172,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a #endif default: { - v.val.i = 0; } break; } @@ -193,7 +179,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } String _get_class_name(JNIEnv *env, jclass cls, bool *array) { - jclass cclass = env->FindClass("java/lang/Class"); jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;"); jstring clsName = (jstring)env->CallObjectMethod(cls, getName); @@ -210,7 +195,6 @@ String _get_class_name(JNIEnv *env, jclass cls, bool *array) { } Variant _jobject_to_variant(JNIEnv *env, jobject obj) { - if (obj == nullptr) { return Variant(); } @@ -220,12 +204,10 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { String name = _get_class_name(env, c, &array); if (name == "java.lang.String") { - return jstring_to_string((jstring)obj, env); }; if (name == "[Ljava.lang.String;") { - jobjectArray arr = (jobjectArray)obj; int stringCount = env->GetArrayLength(arr); Vector<String> sarr; @@ -240,14 +222,12 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "java.lang.Boolean") { - jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z"); bool ret = env->CallBooleanMethod(obj, boolValue); return ret; }; if (name == "java.lang.Integer" || name == "java.lang.Long") { - jclass nclass = env->FindClass("java/lang/Number"); jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J"); jlong ret = env->CallLongMethod(obj, longValue); @@ -255,7 +235,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "[I") { - jintArray arr = (jintArray)obj; int fCount = env->GetArrayLength(arr); Vector<int> sarr; @@ -267,7 +246,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "[B") { - jbyteArray arr = (jbyteArray)obj; int fCount = env->GetArrayLength(arr); Vector<uint8_t> sarr; @@ -279,7 +257,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "java.lang.Float" || name == "java.lang.Double") { - jclass nclass = env->FindClass("java/lang/Number"); jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D"); double ret = env->CallDoubleMethod(obj, doubleValue); @@ -287,7 +264,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "[D") { - jdoubleArray arr = (jdoubleArray)obj; int fCount = env->GetArrayLength(arr); PackedFloat32Array sarr; @@ -296,7 +272,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { real_t *w = sarr.ptrw(); for (int i = 0; i < fCount; i++) { - double n; env->GetDoubleArrayRegion(arr, i, 1, &n); w[i] = n; @@ -305,7 +280,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "[F") { - jfloatArray arr = (jfloatArray)obj; int fCount = env->GetArrayLength(arr); PackedFloat32Array sarr; @@ -314,7 +288,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { real_t *w = sarr.ptrw(); for (int i = 0; i < fCount; i++) { - float n; env->GetFloatArrayRegion(arr, i, 1, &n); w[i] = n; @@ -323,7 +296,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "[Ljava.lang.Object;") { - jobjectArray arr = (jobjectArray)obj; int objCount = env->GetArrayLength(arr); Array varr; @@ -339,7 +311,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { }; if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") { - Dictionary ret; jclass oclass = c; jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;"); @@ -355,7 +326,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { env->DeleteLocalRef(arr); for (int i = 0; i < keys.size(); i++) { - ret[keys[i]] = vals[i]; }; @@ -368,7 +338,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { } Variant::Type get_jni_type(const String &p_type) { - static struct { const char *name; Variant::Type type; @@ -390,7 +359,6 @@ Variant::Type get_jni_type(const String &p_type) { int idx = 0; while (_type_to_vtype[idx].name) { - if (p_type == _type_to_vtype[idx].name) return _type_to_vtype[idx].type; @@ -401,7 +369,6 @@ Variant::Type get_jni_type(const String &p_type) { } const char *get_jni_sig(const String &p_type) { - static struct { const char *name; const char *sig; @@ -423,7 +390,6 @@ const char *get_jni_sig(const String &p_type) { int idx = 0; while (_type_to_vtype[idx].name) { - if (p_type == _type_to_vtype[idx].name) return _type_to_vtype[idx].sig; diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h index b9ee243308..5320715853 100644 --- a/platform/android/jni_utils.h +++ b/platform/android/jni_utils.h @@ -37,7 +37,6 @@ #include <jni.h> struct jvalret { - jobject obj; jvalue val; jvalret() { obj = nullptr; } @@ -53,190 +52,4 @@ Variant::Type get_jni_type(const String &p_type); const char *get_jni_sig(const String &p_type); -class JNISingleton : public Object { - - GDCLASS(JNISingleton, Object); - - struct MethodData { - - jmethodID method; - Variant::Type ret_type; - Vector<Variant::Type> argtypes; - }; - - jobject instance; - Map<StringName, MethodData> method_map; - -public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - - ERR_FAIL_COND_V(!instance, Variant()); - - r_error.error = Callable::CallError::CALL_OK; - - Map<StringName, MethodData>::Element *E = method_map.find(p_method); - if (!E) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - - int ac = E->get().argtypes.size(); - if (ac < p_argcount) { - - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - if (ac > p_argcount) { - - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - for (int i = 0; i < p_argcount; i++) { - - if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = i; - r_error.expected = E->get().argtypes[i]; - } - } - - jvalue *v = nullptr; - - if (p_argcount) { - - v = (jvalue *)alloca(sizeof(jvalue) * p_argcount); - } - - JNIEnv *env = ThreadAndroid::get_env(); - - int res = env->PushLocalFrame(16); - - ERR_FAIL_COND_V(res != 0, Variant()); - - List<jobject> to_erase; - for (int i = 0; i < p_argcount; i++) { - - jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); - v[i] = vr.val; - if (vr.obj) - to_erase.push_back(vr.obj); - } - - Variant ret; - - switch (E->get().ret_type) { - - case Variant::NIL: { - - env->CallVoidMethodA(instance, E->get().method, v); - } break; - case Variant::BOOL: { - - ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE; - } break; - case Variant::INT: { - - ret = env->CallIntMethodA(instance, E->get().method, v); - } break; - case Variant::FLOAT: { - - ret = env->CallFloatMethodA(instance, E->get().method, v); - } break; - case Variant::STRING: { - - jobject o = env->CallObjectMethodA(instance, E->get().method, v); - ret = jstring_to_string((jstring)o, env); - env->DeleteLocalRef(o); - } break; - case Variant::PACKED_STRING_ARRAY: { - - jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); - - ret = _jobject_to_variant(env, arr); - - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_INT32_ARRAY: { - - jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<int> sarr; - sarr.resize(fCount); - - int *w = sarr.ptrw(); - env->GetIntArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - - jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<float> sarr; - sarr.resize(fCount); - - float *w = sarr.ptrw(); - env->GetFloatArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - -#ifndef _MSC_VER -#warning This is missing 64 bits arrays, I have no idea how to do it in JNI -#endif - case Variant::DICTIONARY: { - - jobject obj = env->CallObjectMethodA(instance, E->get().method, v); - ret = _jobject_to_variant(env, obj); - env->DeleteLocalRef(obj); - - } break; - default: { - - env->PopLocalFrame(nullptr); - ERR_FAIL_V(Variant()); - } break; - } - - while (to_erase.size()) { - env->DeleteLocalRef(to_erase.front()->get()); - to_erase.pop_front(); - } - - env->PopLocalFrame(nullptr); - - return ret; - } - - jobject get_instance() const { - - return instance; - } - void set_instance(jobject p_instance) { - - instance = p_instance; - } - - void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) { - - MethodData md; - md.method = p_method; - md.argtypes = p_args; - md.ret_type = p_ret_type; - method_map[p_name] = md; - } - - JNISingleton() { - instance = nullptr; - } -}; - #endif // JNI_UTILS_H diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp index 320bdd3817..0341ef3ec6 100644 --- a/platform/android/net_socket_android.cpp +++ b/platform/android/net_socket_android.cpp @@ -38,7 +38,6 @@ jmethodID NetSocketAndroid::_multicast_lock_acquire = 0; jmethodID NetSocketAndroid::_multicast_lock_release = 0; void NetSocketAndroid::setup(jobject p_net_utils) { - JNIEnv *env = ThreadAndroid::get_env(); net_utils = env->NewGlobalRef(p_net_utils); @@ -72,11 +71,6 @@ void NetSocketAndroid::make_default() { _create = _create_func; } -NetSocketAndroid::NetSocketAndroid() : - wants_broadcast(false), - multicast_groups(0) { -} - NetSocketAndroid::~NetSocketAndroid() { close(); } diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h index 4fc80d2de1..955d906535 100644 --- a/platform/android/net_socket_android.h +++ b/platform/android/net_socket_android.h @@ -45,15 +45,14 @@ * joins/leaves a multicast group. */ class NetSocketAndroid : public NetSocketPosix { - private: static jobject net_utils; static jclass cls; static jmethodID _multicast_lock_acquire; static jmethodID _multicast_lock_release; - bool wants_broadcast; - int multicast_groups; + bool wants_broadcast = false; + int multicast_groups = 0; static void multicast_lock_acquire(); static void multicast_lock_release(); @@ -71,7 +70,7 @@ public: virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name); virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name); - NetSocketAndroid(); + NetSocketAndroid() {} ~NetSocketAndroid(); }; diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 344377d673..baf6ee952a 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -32,15 +32,11 @@ #include "core/io/file_access_buffered_fa.h" #include "core/project_settings.h" -#if defined(OPENGL_ENABLED) -#include "drivers/gles2/rasterizer_gles2.h" -#endif #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "file_access_android.h" #include "main/main.h" -#include "servers/rendering/rendering_server_raster.h" -#include "servers/rendering/rendering_server_wrap_mt.h" +#include "platform/android/display_server_android.h" #include "dir_access_jandroid.h" #include "file_access_jandroid.h" @@ -60,31 +56,7 @@ public: virtual ~AndroidLogger() {} }; -int OS_Android::get_video_driver_count() const { - - return 2; -} - -const char *OS_Android::get_video_driver_name(int p_driver) const { - - switch (p_driver) { - case VIDEO_DRIVER_GLES2: - return "GLES2"; - } - ERR_FAIL_V_MSG(nullptr, "Invalid video driver index: " + itos(p_driver) + "."); -} -int OS_Android::get_audio_driver_count() const { - - return 1; -} - -const char *OS_Android::get_audio_driver_name(int p_driver) const { - - return "Android"; -} - void OS_Android::initialize_core() { - OS_Unix::initialize_core(); if (use_apk_expansion) @@ -110,71 +82,33 @@ void OS_Android::initialize_core() { NetSocketAndroid::make_default(); } -void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { - - ERR_FAIL_COND(!p_gl_extensions); - gl_extensions = p_gl_extensions; -} - -int OS_Android::get_current_video_driver() const { - return video_driver_index; +void OS_Android::initialize() { + initialize_core(); } -Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +void OS_Android::initialize_joypads() { + Input::get_singleton()->set_fallback_mapping(godot_java->get_input_fallback_mapping()); - // FIXME: Add Vulkan support. Readd fallback code from Vulkan to GLES2? - -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - bool gl_initialization_error = false; - - if (RasterizerGLES2::is_viable() == OK) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - gl_initialization_error = true; - } - - if (gl_initialization_error) { - OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n" - "Please try updating your Android version.", - "Unable to initialize video driver"); - return ERR_UNAVAILABLE; - } - } -#endif - - video_driver_index = p_video_driver; - - rendering_server = memnew(RenderingServerRaster); - if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - rendering_server = memnew(RenderingServerWrapMT(rendering_server, false)); - } - - rendering_server->init(); - - AudioDriverManager::initialize(p_audio_driver); - - input = memnew(InputDefault); - input->set_fallback_mapping(godot_java->get_input_fallback_mapping()); - - return OK; + // This queries/updates the currently connected devices/joypads. + godot_java->init_input_devices(); } void OS_Android::set_main_loop(MainLoop *p_main_loop) { - main_loop = p_main_loop; - input->set_main_loop(p_main_loop); } void OS_Android::delete_main_loop() { - - memdelete(main_loop); + if (main_loop) { + memdelete(main_loop); + main_loop = nullptr; + } } void OS_Android::finalize() { +} - memdelete(input); +OS_Android *OS_Android::get_singleton() { + return (OS_Android *)OS::get_singleton(); } GodotJavaWrapper *OS_Android::get_godot_java() { @@ -185,24 +119,15 @@ GodotIOJavaWrapper *OS_Android::get_godot_io_java() { return godot_io_java; } -void OS_Android::alert(const String &p_alert, const String &p_title) { - - //print("ALERT: %s\n", p_alert.utf8().get_data()); - godot_java->alert(p_alert, p_title); -} - bool OS_Android::request_permission(const String &p_name) { - return godot_java->request_permission(p_name); } bool OS_Android::request_permissions() { - return godot_java->request_permissions(); } Vector<String> OS_Android::get_granted_permissions() const { - return godot_java->get_granted_permissions(); } @@ -212,383 +137,53 @@ Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_han return OK; } -void OS_Android::set_mouse_show(bool p_show) { - - //android has no mouse... -} - -void OS_Android::set_mouse_grab(bool p_grab) { - - //it really has no mouse...! -} - -bool OS_Android::is_mouse_grab_enabled() const { - - //*sigh* technology has evolved so much since i was a kid.. - return false; -} - -Point2 OS_Android::get_mouse_position() const { - - return Point2(); -} - -int OS_Android::get_mouse_button_state() const { - - return 0; -} - -void OS_Android::set_window_title(const String &p_title) { - //This queries/updates the currently connected devices/joypads - //Set_window_title is called when initializing the main loop (main.cpp) - //therefore this place is found to be suitable (I found no better). - godot_java->init_input_devices(); -} - -void OS_Android::set_video_mode(const VideoMode &p_video_mode, int p_screen) { -} - -OS::VideoMode OS_Android::get_video_mode(int p_screen) const { - - return default_videomode; -} - -void OS_Android::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { - - p_list->push_back(default_videomode); -} - -void OS_Android::set_keep_screen_on(bool p_enabled) { - OS::set_keep_screen_on(p_enabled); - - godot_java->set_keep_screen_on(p_enabled); -} - -Size2 OS_Android::get_window_size() const { - - return Vector2(default_videomode.width, default_videomode.height); -} - String OS_Android::get_name() const { - return "Android"; } MainLoop *OS_Android::get_main_loop() const { - return main_loop; } -bool OS_Android::can_draw() const { - - return true; //always? -} - void OS_Android::main_loop_begin() { - if (main_loop) main_loop->init(); } bool OS_Android::main_loop_iterate() { - if (!main_loop) return false; return Main::iteration(); } void OS_Android::main_loop_end() { - if (main_loop) main_loop->finish(); } void OS_Android::main_loop_focusout() { - - if (main_loop) - main_loop->notification(NOTIFICATION_WM_FOCUS_OUT); + DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT); audio_driver_android.set_pause(true); } void OS_Android::main_loop_focusin() { - - if (main_loop) - main_loop->notification(NOTIFICATION_WM_FOCUS_IN); + DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN); audio_driver_android.set_pause(false); } -void OS_Android::process_joy_event(OS_Android::JoypadEvent p_event) { - - switch (p_event.type) { - case JOY_EVENT_BUTTON: - input->joy_button(p_event.device, p_event.index, p_event.pressed); - break; - case JOY_EVENT_AXIS: - InputDefault::JoyAxis value; - value.min = -1; - value.value = p_event.value; - input->joy_axis(p_event.device, p_event.index, value); - break; - case JOY_EVENT_HAT: - input->joy_hat(p_event.device, p_event.hat); - break; - default: - return; - } -} - -void OS_Android::process_event(Ref<InputEvent> p_event) { - - input->parse_input_event(p_event); -} - -void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points) { - - switch (p_what) { - case 0: { //gesture begin - - if (touch.size()) { - //end all if exist - for (int i = 0; i < touch.size(); i++) { - - Ref<InputEventScreenTouch> ev; - ev.instance(); - ev->set_index(touch[i].id); - ev->set_pressed(false); - ev->set_position(touch[i].pos); - input->parse_input_event(ev); - } - } - - touch.resize(p_points.size()); - for (int i = 0; i < p_points.size(); i++) { - touch.write[i].id = p_points[i].id; - touch.write[i].pos = p_points[i].pos; - } - - //send touch - for (int i = 0; i < touch.size(); i++) { - - Ref<InputEventScreenTouch> ev; - ev.instance(); - ev->set_index(touch[i].id); - ev->set_pressed(true); - ev->set_position(touch[i].pos); - input->parse_input_event(ev); - } - - } break; - case 1: { //motion - - ERR_FAIL_COND(touch.size() != p_points.size()); - - for (int i = 0; i < touch.size(); i++) { - - int idx = -1; - for (int j = 0; j < p_points.size(); j++) { - - if (touch[i].id == p_points[j].id) { - idx = j; - break; - } - } - - ERR_CONTINUE(idx == -1); - - if (touch[i].pos == p_points[idx].pos) - continue; //no move unncesearily - - Ref<InputEventScreenDrag> ev; - ev.instance(); - ev->set_index(touch[i].id); - ev->set_position(p_points[idx].pos); - ev->set_relative(p_points[idx].pos - touch[i].pos); - input->parse_input_event(ev); - touch.write[i].pos = p_points[idx].pos; - } - - } break; - case 2: { //release - - if (touch.size()) { - //end all if exist - for (int i = 0; i < touch.size(); i++) { - - Ref<InputEventScreenTouch> ev; - ev.instance(); - ev->set_index(touch[i].id); - ev->set_pressed(false); - ev->set_position(touch[i].pos); - input->parse_input_event(ev); - } - touch.clear(); - } - } break; - case 3: { // add touch - - for (int i = 0; i < p_points.size(); i++) { - if (p_points[i].id == p_pointer) { - TouchPos tp = p_points[i]; - touch.push_back(tp); - - Ref<InputEventScreenTouch> ev; - ev.instance(); - - ev->set_index(tp.id); - ev->set_pressed(true); - ev->set_position(tp.pos); - input->parse_input_event(ev); - - break; - } - } - } break; - case 4: { // remove touch - - for (int i = 0; i < touch.size(); i++) { - if (touch[i].id == p_pointer) { - - Ref<InputEventScreenTouch> ev; - ev.instance(); - ev->set_index(touch[i].id); - ev->set_pressed(false); - ev->set_position(touch[i].pos); - input->parse_input_event(ev); - touch.remove(i); - - break; - } - } - } break; - } -} - -void OS_Android::process_hover(int p_type, Point2 p_pos) { - // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER - switch (p_type) { - case 7: // hover move - case 9: // hover enter - case 10: { // hover exit - Ref<InputEventMouseMotion> ev; - ev.instance(); - ev->set_position(p_pos); - ev->set_global_position(p_pos); - ev->set_relative(p_pos - hover_prev_pos); - input->parse_input_event(ev); - hover_prev_pos = p_pos; - } break; - } -} - -void OS_Android::process_double_tap(Point2 p_pos) { - Ref<InputEventMouseButton> ev; - ev.instance(); - ev->set_position(p_pos); - ev->set_global_position(p_pos); - ev->set_pressed(false); - ev->set_doubleclick(true); - input->parse_input_event(ev); -} - -void OS_Android::process_scroll(Point2 p_pos) { - Ref<InputEventPanGesture> ev; - ev.instance(); - ev->set_position(p_pos); - ev->set_delta(p_pos - scroll_prev_pos); - input->parse_input_event(ev); - scroll_prev_pos = p_pos; -} - -void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) { - - input->set_accelerometer(p_accelerometer); -} - -void OS_Android::process_gravity(const Vector3 &p_gravity) { - - input->set_gravity(p_gravity); -} - -void OS_Android::process_magnetometer(const Vector3 &p_magnetometer) { - - input->set_magnetometer(p_magnetometer); -} - -void OS_Android::process_gyroscope(const Vector3 &p_gyroscope) { - - input->set_gyroscope(p_gyroscope); -} - -bool OS_Android::has_touchscreen_ui_hint() const { - - return true; -} - -bool OS_Android::has_virtual_keyboard() const { - - return true; -} - -int OS_Android::get_virtual_keyboard_height() const { - return godot_io_java->get_vk_height(); - - // ERR_PRINT("Cannot obtain virtual keyboard height."); - // return 0; -} - -void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) { - - if (godot_io_java->has_vk()) { - godot_io_java->show_vk(p_existing_text, p_max_input_length); - } else { - - ERR_PRINT("Virtual keyboard not available"); - }; -} - -void OS_Android::hide_virtual_keyboard() { - - if (godot_io_java->has_vk()) { - - godot_io_java->hide_vk(); - } else { - - ERR_PRINT("Virtual keyboard not available"); - }; -} - -void OS_Android::init_video_mode(int p_video_width, int p_video_height) { - - default_videomode.width = p_video_width; - default_videomode.height = p_video_height; - default_videomode.fullscreen = true; - default_videomode.resizable = false; -} - void OS_Android::main_loop_request_go_back() { - - if (main_loop) - main_loop->notification(NOTIFICATION_WM_GO_BACK_REQUEST); -} - -void OS_Android::set_display_size(Size2 p_size) { - - default_videomode.width = p_size.x; - default_videomode.height = p_size.y; + DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST); } Error OS_Android::shell_open(String p_uri) { - return godot_io_java->open_uri(p_uri); } String OS_Android::get_resource_dir() const { - return "/"; //android has its own filesystem for resources inside the APK } String OS_Android::get_locale() const { - String locale = godot_io_java->get_locale(); if (locale != "") { return locale; @@ -597,28 +192,7 @@ String OS_Android::get_locale() const { return OS_Unix::get_locale(); } -void OS_Android::set_clipboard(const String &p_text) { - - // DO we really need the fallback to OS_Unix here?! - if (godot_java->has_set_clipboard()) { - godot_java->set_clipboard(p_text); - } else { - OS_Unix::set_clipboard(p_text); - } -} - -String OS_Android::get_clipboard() const { - - // DO we really need the fallback to OS_Unix here?! - if (godot_java->has_get_clipboard()) { - return godot_java->get_clipboard(); - } - - return OS_Unix::get_clipboard(); -} - String OS_Android::get_model_name() const { - String model = godot_io_java->get_model(); if (model != "") return model; @@ -626,19 +200,12 @@ String OS_Android::get_model_name() const { return OS_Unix::get_model_name(); } -int OS_Android::get_screen_dpi(int p_screen) const { - - return godot_io_java->get_screen_dpi(); -} - String OS_Android::get_user_data_dir() const { - if (data_dir_cache != String()) return data_dir_cache; String data_dir = godot_io_java->get_user_data_dir(); if (data_dir != "") { - //store current dir char real_current_dir_name[2048]; getcwd(real_current_dir_name, 2048); @@ -662,13 +229,7 @@ String OS_Android::get_user_data_dir() const { return "."; } -void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { - - godot_io_java->set_screen_orientation(p_orientation); -} - String OS_Android::get_unique_id() const { - String unique_id = godot_io_java->get_unique_id(); if (unique_id != "") return unique_id; @@ -676,50 +237,45 @@ String OS_Android::get_unique_id() const { return OS::get_unique_id(); } -Error OS_Android::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { - // FIXME: Add support for volume, audio and subtitle tracks - - godot_io_java->play_video(p_path); - return OK; -} - -bool OS_Android::native_video_is_playing() const { - - return godot_io_java->is_video_playing(); -} - -void OS_Android::native_video_pause() { - - godot_io_java->pause_video(); -} - String OS_Android::get_system_dir(SystemDir p_dir) const { - return godot_io_java->get_system_dir(p_dir); } -void OS_Android::native_video_stop() { +void OS_Android::set_display_size(const Size2i &p_size) { + display_size = p_size; +} - godot_io_java->stop_video(); +Size2i OS_Android::get_display_size() const { + return display_size; } void OS_Android::set_context_is_16_bits(bool p_is_16) { - +#if defined(OPENGL_ENABLED) //use_16bits_fbo = p_is_16; //if (rasterizer) // rasterizer->set_force_16_bits_fbo(p_is_16); +#endif } -void OS_Android::joy_connection_changed(int p_device, bool p_connected, String p_name) { - return input->joy_connection_changed(p_device, p_connected, p_name, ""); +void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { +#if defined(OPENGL_ENABLED) + ERR_FAIL_COND(!p_gl_extensions); + gl_extensions = p_gl_extensions; +#endif } -bool OS_Android::is_joy_known(int p_device) { - return input->is_joy_mapped(p_device); +void OS_Android::set_native_window(ANativeWindow *p_native_window) { +#if defined(VULKAN_ENABLED) + native_window = p_native_window; +#endif } -String OS_Android::get_joy_guid(int p_device) const { - return input->get_joy_guid_remapped(p_device); +ANativeWindow *OS_Android::get_native_window() const { +#if defined(VULKAN_ENABLED) + return native_window; +#else + return nullptr; +#endif } void OS_Android::vibrate_handheld(int p_duration_ms) { @@ -747,19 +303,21 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { } OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion) { + display_size.width = 800; + display_size.height = 600; use_apk_expansion = p_use_apk_expansion; - default_videomode.width = 800; - default_videomode.height = 600; - default_videomode.fullscreen = true; - default_videomode.resizable = false; main_loop = nullptr; + +#if defined(OPENGL_ENABLED) gl_extensions = nullptr; - //rasterizer = nullptr; use_gl2 = false; +#endif - rendering_server = nullptr; +#if defined(VULKAN_ENABLED) + native_window = nullptr; +#endif godot_java = p_godot_java; godot_io_java = p_godot_io_java; @@ -769,6 +327,8 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god _set_logger(memnew(CompositeLogger(loggers))); AudioDriverManager::add_driver(&audio_driver_android); + + DisplayServerAndroid::register_android_driver(); } OS_Android::~OS_Android() { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 8a91412ef6..cac7efaa88 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -33,78 +33,45 @@ #include "audio_driver_jandroid.h" #include "audio_driver_opensl.h" -#include "core/input/input_filter.h" #include "core/os/main_loop.h" #include "drivers/unix/os_unix.h" #include "servers/audio_server.h" -#include "servers/rendering/rasterizer.h" class GodotJavaWrapper; class GodotIOJavaWrapper; -class OS_Android : public OS_Unix { -public: - struct TouchPos { - int id; - Point2 pos; - }; - - enum { - JOY_EVENT_BUTTON = 0, - JOY_EVENT_AXIS = 1, - JOY_EVENT_HAT = 2 - }; - - struct JoypadEvent { - - int device; - int type; - int index; - bool pressed; - float value; - int hat; - }; +struct ANativeWindow; +class OS_Android : public OS_Unix { private: - Vector<TouchPos> touch; - Point2 hover_prev_pos; // needed to calculate the relative position on hover events - Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events + Size2i display_size; - bool use_gl2; bool use_apk_expansion; +#if defined(OPENGL_ENABLED) bool use_16bits_fbo; + const char *gl_extensions; +#endif - RenderingServer *rendering_server; +#if defined(VULKAN_ENABLED) + ANativeWindow *native_window; +#endif mutable String data_dir_cache; //AudioDriverAndroid audio_driver_android; AudioDriverOpenSL audio_driver_android; - const char *gl_extensions; - - InputDefault *input; - VideoMode default_videomode; MainLoop *main_loop; GodotJavaWrapper *godot_java; GodotIOJavaWrapper *godot_io_java; - int video_driver_index; - public: - // functions used by main to initialize/deinitialize the OS - virtual int get_video_driver_count() const; - virtual const char *get_video_driver_name(int p_driver) const; - - virtual int get_audio_driver_count() const; - virtual const char *get_audio_driver_name(int p_driver) const; - - virtual int get_current_video_driver() const; - virtual void initialize_core(); - virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual void initialize(); + + virtual void initialize_joypads(); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); @@ -113,37 +80,19 @@ public: typedef int64_t ProcessID; - static OS *get_singleton(); + static OS_Android *get_singleton(); GodotJavaWrapper *get_godot_java(); GodotIOJavaWrapper *get_godot_io_java(); - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual bool request_permission(const String &p_name); virtual bool request_permissions(); virtual Vector<String> get_granted_permissions() const; virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); - virtual void set_mouse_show(bool p_show); - virtual void set_mouse_grab(bool p_grab); - virtual bool is_mouse_grab_enabled() const; - virtual Point2 get_mouse_position() const; - virtual int get_mouse_button_state() const; - virtual void set_window_title(const String &p_title); - - virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); - virtual VideoMode get_video_mode(int p_screen = 0) const; - virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const; - - virtual void set_keep_screen_on(bool p_enabled); - - virtual Size2 get_window_size() const; - virtual String get_name() const; virtual MainLoop *get_main_loop() const; - virtual bool can_draw() const; - void main_loop_begin(); bool main_loop_iterate(); void main_loop_request_go_back(); @@ -151,53 +100,25 @@ public: void main_loop_focusout(); void main_loop_focusin(); - virtual bool has_touchscreen_ui_hint() const; - - virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1); - virtual void hide_virtual_keyboard(); - virtual int get_virtual_keyboard_height() const; - - void set_opengl_extensions(const char *p_gl_extensions); - void set_display_size(Size2 p_size); + void set_display_size(const Size2i &p_size); + Size2i get_display_size() const; void set_context_is_16_bits(bool p_is_16); + void set_opengl_extensions(const char *p_gl_extensions); - virtual void set_screen_orientation(ScreenOrientation p_orientation); + void set_native_window(ANativeWindow *p_native_window); + ANativeWindow *get_native_window() const; virtual Error shell_open(String p_uri); virtual String get_user_data_dir() const; virtual String get_resource_dir() const; virtual String get_locale() const; - virtual void set_clipboard(const String &p_text); - virtual String get_clipboard() const; virtual String get_model_name() const; - virtual int get_screen_dpi(int p_screen = 0) const; virtual String get_unique_id() const; virtual String get_system_dir(SystemDir p_dir) const; - void process_accelerometer(const Vector3 &p_accelerometer); - void process_gravity(const Vector3 &p_gravity); - void process_magnetometer(const Vector3 &p_magnetometer); - void process_gyroscope(const Vector3 &p_gyroscope); - void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); - void process_hover(int p_type, Point2 p_pos); - void process_double_tap(Point2 p_pos); - void process_scroll(Point2 p_pos); - void process_joy_event(JoypadEvent p_event); - void process_event(Ref<InputEvent> p_event); - void init_video_mode(int p_video_width, int p_video_height); - - virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); - virtual bool native_video_is_playing() const; - virtual void native_video_pause(); - virtual void native_video_stop(); - - virtual bool is_joy_known(int p_device); - virtual String get_joy_guid(int p_device) const; - void joy_connection_changed(int p_device, bool p_connected, String p_name); void vibrate_handheld(int p_duration_ms); virtual bool _check_internal_feature_support(const String &p_feature); diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h new file mode 100644 index 0000000000..9ad7de1202 --- /dev/null +++ b/platform/android/plugin/godot_plugin_config.h @@ -0,0 +1,251 @@ +/*************************************************************************/ +/* godot_plugin_config.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GODOT_PLUGIN_CONFIG_H +#define GODOT_PLUGIN_CONFIG_H + +#include "core/error_list.h" +#include "core/io/config_file.h" +#include "core/ustring.h" + +static const char *PLUGIN_CONFIG_EXT = ".gdap"; + +static const char *CONFIG_SECTION = "config"; +static const char *CONFIG_NAME_KEY = "name"; +static const char *CONFIG_BINARY_TYPE_KEY = "binary_type"; +static const char *CONFIG_BINARY_KEY = "binary"; + +static const char *DEPENDENCIES_SECTION = "dependencies"; +static const char *DEPENDENCIES_LOCAL_KEY = "local"; +static const char *DEPENDENCIES_REMOTE_KEY = "remote"; +static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos"; + +static const char *BINARY_TYPE_LOCAL = "local"; +static const char *BINARY_TYPE_REMOTE = "remote"; + +static const char *PLUGIN_VALUE_SEPARATOR = "|"; + +/* + The `config` section and fields are required and defined as follow: +- **name**: name of the plugin +- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field +- **binary**: + - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`). + - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0"). + +The `dependencies` section and fields are optional and defined as follow: +- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory. +- **remote**: contains a list of remote binary gradle dependencies for the plugin. +- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies. + + See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871 + */ +struct PluginConfig { + // Set to true when the config file is properly loaded. + bool valid_config = false; + + // Required config section + String name; + String binary_type; + String binary; + + // Optional dependencies section + Vector<String> local_dependencies; + Vector<String> remote_dependencies; + Vector<String> custom_maven_repos; +}; + +/* + * Set of prebuilt plugins. + */ +static const PluginConfig GODOT_PAYMENT = { + /*.valid_config =*/true, + /*.name =*/"GodotPayment", + /*.binary_type =*/"local", + /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", + /*.local_dependencies =*/{}, + /*.remote_dependencies =*/{}, + /*.custom_maven_repos =*/{} +}; + +static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { + String absolute_path; + if (!dependency_path.empty()) { + if (dependency_path.is_abs_path()) { + absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path); + } else { + absolute_path = plugin_config_dir.plus_file(dependency_path); + } + } + + return absolute_path; +} + +static inline PluginConfig resolve_prebuilt_plugin(PluginConfig prebuilt_plugin, String plugin_config_dir) { + PluginConfig resolved = prebuilt_plugin; + resolved.binary = resolved.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary; + if (!prebuilt_plugin.local_dependencies.empty()) { + resolved.local_dependencies.clear(); + for (int i = 0; i < prebuilt_plugin.local_dependencies.size(); i++) { + resolved.local_dependencies.push_back(resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.local_dependencies[i])); + } + } + return resolved; +} + +static inline Vector<PluginConfig> get_prebuilt_plugins(String plugins_base_dir) { + Vector<PluginConfig> prebuilt_plugins; + prebuilt_plugins.push_back(resolve_prebuilt_plugin(GODOT_PAYMENT, plugins_base_dir)); + return prebuilt_plugins; +} + +static inline bool is_plugin_config_valid(PluginConfig plugin_config) { + bool valid_name = !plugin_config.name.empty(); + bool valid_binary_type = plugin_config.binary_type == BINARY_TYPE_LOCAL || + plugin_config.binary_type == BINARY_TYPE_REMOTE; + + bool valid_binary = false; + if (valid_binary_type) { + valid_binary = !plugin_config.binary.empty() && + (plugin_config.binary_type == BINARY_TYPE_REMOTE || + FileAccess::exists(plugin_config.binary)); + } + + bool valid_local_dependencies = true; + if (!plugin_config.local_dependencies.empty()) { + for (int i = 0; i < plugin_config.local_dependencies.size(); i++) { + if (!FileAccess::exists(plugin_config.local_dependencies[i])) { + valid_local_dependencies = false; + break; + } + } + } + return valid_name && valid_binary && valid_binary_type && valid_local_dependencies; +} + +static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) { + PluginConfig plugin_config = {}; + + if (config_file.is_valid()) { + Error err = config_file->load(path); + if (err == OK) { + String config_base_dir = path.get_base_dir(); + + plugin_config.name = config_file->get_value(CONFIG_SECTION, CONFIG_NAME_KEY, String()); + plugin_config.binary_type = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_TYPE_KEY, String()); + + String binary_path = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_KEY, String()); + plugin_config.binary = plugin_config.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path; + + if (config_file->has_section(DEPENDENCIES_SECTION)) { + Vector<String> local_dependencies_paths = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_LOCAL_KEY, Vector<String>()); + if (!local_dependencies_paths.empty()) { + for (int i = 0; i < local_dependencies_paths.size(); i++) { + plugin_config.local_dependencies.push_back(resolve_local_dependency_path(config_base_dir, local_dependencies_paths[i])); + } + } + + plugin_config.remote_dependencies = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_REMOTE_KEY, Vector<String>()); + plugin_config.custom_maven_repos = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>()); + } + + plugin_config.valid_config = is_plugin_config_valid(plugin_config); + } + } + + return plugin_config; +} + +static inline String get_plugins_binaries(String binary_type, Vector<PluginConfig> plugins_configs) { + String plugins_binaries; + if (!plugins_configs.empty()) { + Vector<String> binaries; + for (int i = 0; i < plugins_configs.size(); i++) { + PluginConfig config = plugins_configs[i]; + if (!config.valid_config) { + continue; + } + + if (config.binary_type == binary_type) { + binaries.push_back(config.binary); + } + + if (binary_type == BINARY_TYPE_LOCAL) { + binaries.append_array(config.local_dependencies); + } + + if (binary_type == BINARY_TYPE_REMOTE) { + binaries.append_array(config.remote_dependencies); + } + } + + plugins_binaries = String(PLUGIN_VALUE_SEPARATOR).join(binaries); + } + + return plugins_binaries; +} + +static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins_configs) { + String custom_maven_repos; + if (!plugins_configs.empty()) { + Vector<String> repos_urls; + for (int i = 0; i < plugins_configs.size(); i++) { + PluginConfig config = plugins_configs[i]; + if (!config.valid_config) { + continue; + } + + repos_urls.append_array(config.custom_maven_repos); + } + + custom_maven_repos = String(PLUGIN_VALUE_SEPARATOR).join(repos_urls); + } + return custom_maven_repos; +} + +static inline String get_plugins_names(Vector<PluginConfig> plugins_configs) { + String plugins_names; + if (!plugins_configs.empty()) { + Vector<String> names; + for (int i = 0; i < plugins_configs.size(); i++) { + PluginConfig config = plugins_configs[i]; + if (!config.valid_config) { + continue; + } + + names.push_back(config.name); + } + plugins_names = String(PLUGIN_VALUE_SEPARATOR).join(names); + } + + return plugins_names; +} + +#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 7413236e5d..557743fa73 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -33,6 +33,7 @@ #include <core/engine.h> #include <core/error_macros.h> #include <core/project_settings.h> +#include <platform/android/api/jni_singleton.h> #include <platform/android/jni_utils.h> #include <platform/android/string_android.h> @@ -41,9 +42,8 @@ static HashMap<String, JNISingleton *> jni_singletons; extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) { - String singname = jstring_to_string(name, env); - JNISingleton *s = memnew(JNISingleton); + JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton"); s->set_instance(env->NewGlobalRef(obj)); jni_singletons[singname] = s; @@ -52,7 +52,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis } JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { - String singname = jstring_to_string(sname, env); ERR_FAIL_COND(!jni_singletons.has(singname)); @@ -67,7 +66,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis int stringCount = env->GetArrayLength(args); for (int i = 0; i < stringCount; i++) { - jstring string = (jstring)env->GetObjectArrayElement(args, i); const String rawString = jstring_to_string(string, env); types.push_back(get_jni_type(rawString)); @@ -79,13 +77,55 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis jclass cls = env->GetObjectClass(s->get_instance()); jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); if (!mid) { - print_line("Failed getting method ID " + mname); } s->add_method(mname, mid, types, get_jni_type(retval)); } +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) { + String singleton_name = jstring_to_string(j_plugin_name, env); + + ERR_FAIL_COND(!jni_singletons.has(singleton_name)); + + JNISingleton *singleton = jni_singletons.get(singleton_name); + + String signal_name = jstring_to_string(j_signal_name, env); + Vector<Variant::Type> types; + + int stringCount = env->GetArrayLength(j_signal_param_types); + + for (int i = 0; i < stringCount; i++) { + jstring j_signal_param_type = (jstring)env->GetObjectArrayElement(j_signal_param_types, i); + const String signal_param_type = jstring_to_string(j_signal_param_type, env); + types.push_back(get_jni_type(signal_param_type)); + } + + singleton->add_signal(signal_name, types); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) { + String singleton_name = jstring_to_string(j_plugin_name, env); + + ERR_FAIL_COND(!jni_singletons.has(singleton_name)); + + JNISingleton *singleton = jni_singletons.get(singleton_name); + + String signal_name = jstring_to_string(j_signal_name, env); + + int count = env->GetArrayLength(j_signal_params); + const Variant *args[count]; + + for (int i = 0; i < count; i++) { + jobject j_param = env->GetObjectArrayElement(j_signal_params, i); + Variant variant = _jobject_to_variant(env, j_param); + args[i] = &variant; + env->DeleteLocalRef(j_param); + }; + + singleton->emit_signal(signal_name, args, count); +} + JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) { int gdnlib_count = env->GetArrayLength(gdnlib_paths); if (gdnlib_count == 0) { diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h index 0d613d3bfe..80ce332e7c 100644 --- a/platform/android/plugin/godot_plugin_jni.h +++ b/platform/android/plugin/godot_plugin_jni.h @@ -37,6 +37,8 @@ extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, 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, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths); } diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index 729327f6f0..13aa313ebf 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -48,17 +48,14 @@ pthread_key_t ThreadAndroid::thread_id_key = _create_thread_id_key(); Thread::ID ThreadAndroid::next_thread_id = 0; Thread::ID ThreadAndroid::get_id() const { - return id; } Thread *ThreadAndroid::create_thread_jandroid() { - return memnew(ThreadAndroid); } void *ThreadAndroid::thread_callback(void *userdata) { - ThreadAndroid *t = reinterpret_cast<ThreadAndroid *>(userdata); setup_thread(); ScriptServer::thread_enter(); //scripts may need to attach a stack @@ -70,7 +67,6 @@ void *ThreadAndroid::thread_callback(void *userdata) { } Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, void *p_user, const Settings &) { - ThreadAndroid *tr = memnew(ThreadAndroid); tr->callback = p_callback; tr->user = p_user; @@ -83,7 +79,6 @@ Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, voi } Thread::ID ThreadAndroid::get_thread_id_func_jandroid() { - void *value = pthread_getspecific(thread_id_key); if (value) @@ -95,7 +90,6 @@ Thread::ID ThreadAndroid::get_thread_id_func_jandroid() { } void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) { - ThreadAndroid *tp = static_cast<ThreadAndroid *>(p_thread); ERR_FAIL_COND(!tp); ERR_FAIL_COND(tp->pthread == 0); @@ -105,7 +99,6 @@ void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) { } void ThreadAndroid::_thread_destroyed(void *value) { - /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ JNIEnv *env = (JNIEnv *)value; if (env != nullptr) { @@ -118,7 +111,6 @@ pthread_key_t ThreadAndroid::jvm_key; JavaVM *ThreadAndroid::java_vm = nullptr; void ThreadAndroid::setup_thread() { - if (pthread_getspecific(jvm_key)) return; //already setup JNIEnv *env; @@ -127,7 +119,6 @@ void ThreadAndroid::setup_thread() { } void ThreadAndroid::make_default(JavaVM *p_java_vm) { - java_vm = p_java_vm; create_func = create_func_jandroid; get_thread_id_func = get_thread_id_func_jandroid; @@ -137,7 +128,6 @@ void ThreadAndroid::make_default(JavaVM *p_java_vm) { } JNIEnv *ThreadAndroid::get_env() { - if (!pthread_getspecific(jvm_key)) { setup_thread(); } @@ -148,7 +138,6 @@ JNIEnv *ThreadAndroid::get_env() { } ThreadAndroid::ThreadAndroid() { - pthread = 0; } diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h index eb4725ae68..9cfcc64813 100644 --- a/platform/android/thread_jandroid.h +++ b/platform/android/thread_jandroid.h @@ -37,7 +37,6 @@ #include <sys/types.h> class ThreadAndroid : public Thread { - static pthread_key_t thread_id_key; static ID next_thread_id; diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp new file mode 100644 index 0000000000..5fb7a83da4 --- /dev/null +++ b/platform/android/vulkan/vulkan_context_android.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* vulkan_context_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "vulkan_context_android.h" +#include <vulkan/vulkan_android.h> + +const char *VulkanContextAndroid::_get_platform_surface_extension() const { + return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; +} + +int VulkanContextAndroid::window_create(ANativeWindow *p_window, int p_width, int p_height) { + VkAndroidSurfaceCreateInfoKHR createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.window = p_window; + + VkSurfaceKHR surface; + VkResult err = vkCreateAndroidSurfaceKHR(_get_instance(), &createInfo, nullptr, &surface); + if (err != VK_SUCCESS) { + ERR_FAIL_V_MSG(-1, "vkCreateAndroidSurfaceKHR failed with error " + itos(err)); + } + + return _window_create(DisplayServer::MAIN_WINDOW_ID, surface, p_width, p_height); +} + +VulkanContextAndroid::VulkanContextAndroid() { + // TODO: fix validation layers + use_validation_layers = false; +} + +VulkanContextAndroid::~VulkanContextAndroid() { +} diff --git a/platform/android/vulkan/vk_renderer_jni.cpp b/platform/android/vulkan/vulkan_context_android.h index 3026e7daad..6bd3cbee36 100644 --- a/platform/android/vulkan/vk_renderer_jni.cpp +++ b/platform/android/vulkan/vulkan_context_android.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* vk_renderer_jni.cpp */ +/* vulkan_context_android.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vk_renderer_jni.h" +#ifndef VULKAN_CONTEXT_ANDROID_H +#define VULKAN_CONTEXT_ANDROID_H -extern "C" { +#include "drivers/vulkan/vulkan_context.h" -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface) { - // TODO: complete -} +struct ANativeWindow; -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height) { - // TODO: complete -} +class VulkanContextAndroid : public VulkanContext { + virtual const char *_get_platform_surface_extension() const; -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj) { - // TODO: complete -} +public: + int window_create(ANativeWindow *p_window, int p_width, int p_height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj) { - // TODO: complete -} + VulkanContextAndroid(); + ~VulkanContextAndroid(); +}; -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj) { - // TODO: complete -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj) { - // TODO: complete -} -} +#endif // VULKAN_CONTEXT_ANDROID_H |