diff options
Diffstat (limited to 'platform/android/api')
-rw-r--r-- | platform/android/api/api.cpp | 14 | ||||
-rw-r--r-- | platform/android/api/api.h | 4 | ||||
-rw-r--r-- | platform/android/api/java_class_wrapper.h | 53 | ||||
-rw-r--r-- | platform/android/api/jni_singleton.h | 223 |
4 files changed, 264 insertions, 30 deletions
diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp index ef11b12971..d3c49c6eb7 100644 --- a/platform/android/api/api.cpp +++ b/platform/android/api/api.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -30,17 +30,21 @@ #include "api.h" -#include "core/engine.h" +#include "core/config/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/api.h b/platform/android/api/api.h index 5e951b9c88..fe3a6734ac 100644 --- a/platform/android/api/api.h +++ b/platform/android/api/api.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h index 59fcd94b4d..d6c7a1abe5 100644 --- a/platform/android/api/java_class_wrapper.h +++ b/platform/android/api/java_class_wrapper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,7 +31,7 @@ #ifndef JAVA_CLASS_WRAPPER_H #define JAVA_CLASS_WRAPPER_H -#include "core/reference.h" +#include "core/object/reference.h" #ifdef ANDROID_ENABLED #include <android/log.h> @@ -43,12 +43,10 @@ class JavaObject; #endif class JavaClass : public Reference { - GDCLASS(JavaClass, Reference); #ifdef ANDROID_ENABLED enum ArgumentType{ - ARG_TYPE_VOID, ARG_TYPE_BOOLEAN, ARG_TYPE_BYTE, @@ -68,24 +66,25 @@ class JavaClass : public Reference { Map<StringName, Variant> constant_map; struct MethodInfo { - - bool _static; + bool _static = false; Vector<uint32_t> param_types; Vector<StringName> param_sigs; - uint32_t return_type; + uint32_t return_type = 0; jmethodID method; }; _FORCE_INLINE_ static void _convert_to_variant_type(int p_sig, Variant::Type &r_type, float &likelihood) { - likelihood = 1.0; r_type = Variant::NIL; switch (p_sig) { - - case ARG_TYPE_VOID: r_type = Variant::NIL; break; + case ARG_TYPE_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 +120,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 +160,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; } } @@ -168,13 +179,12 @@ class JavaClass : public Reference { #endif public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; JavaClass(); }; class JavaObject : public Reference { - GDCLASS(JavaObject, Reference); #ifdef ANDROID_ENABLED @@ -185,7 +195,7 @@ class JavaObject : public Reference { #endif public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; #ifdef ANDROID_ENABLED JavaObject(const Ref<JavaClass> &p_base, jobject *p_instance); @@ -194,7 +204,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..965eaabf81 --- /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-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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/config/engine.h> +#include <core/variant/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) override { +#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 = get_jni_env(); + + int res = env->PushLocalFrame(16); + + ERR_FAIL_COND_V(res != 0, Variant()); + + List<jobject> to_erase; + for (int i = 0; i < p_argcount; i++) { + jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); + v[i] = vr.val; + if (vr.obj) + to_erase.push_back(vr.obj); + } + + Variant ret; + + switch (E->get().ret_type) { + case Variant::NIL: { + env->CallVoidMethodA(instance, E->get().method, v); + } break; + case Variant::BOOL: { + ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE; + } break; + case Variant::INT: { + ret = env->CallIntMethodA(instance, E->get().method, v); + } break; + case Variant::FLOAT: { + ret = env->CallFloatMethodA(instance, E->get().method, v); + } break; + case Variant::STRING: { + jobject o = env->CallObjectMethodA(instance, E->get().method, v); + ret = jstring_to_string((jstring)o, env); + env->DeleteLocalRef(o); + } break; + case Variant::PACKED_STRING_ARRAY: { + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); + + ret = _jobject_to_variant(env, arr); + + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_INT32_ARRAY: { + jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + Vector<int> sarr; + sarr.resize(fCount); + + int *w = sarr.ptrw(); + env->GetIntArrayRegion(arr, 0, fCount, w); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_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 |