diff --git a/doc/classes/JavaClass.xml b/doc/classes/JavaClass.xml index ecfcaa87815..9a6c30df10e 100644 --- a/doc/classes/JavaClass.xml +++ b/doc/classes/JavaClass.xml @@ -1,13 +1,33 @@ - Represents an object from the Java Native Interface. + Represents a class from the Java Native Interface. - Represents an object from the Java Native Interface. It is returned from [method JavaClassWrapper.wrap]. - [b]Note:[/b] This class only works on Android. For any other build, this class does nothing. + Represents a class from the Java Native Interface. It is returned from [method JavaClassWrapper.wrap]. + [b]Note:[/b] This class only works on Android. On any other platform, this class does nothing. [b]Note:[/b] This class is not to be confused with [JavaScriptObject]. + + + + + Returns the Java class name. + + + + + + Returns the object's Java methods and their signatures as an [Array] of dictionaries, in the same format as [method Object.get_method_list]. + + + + + + Returns a [JavaClass] representing the Java parent class of this class. + + + diff --git a/doc/classes/JavaClassWrapper.xml b/doc/classes/JavaClassWrapper.xml index 01c3392b049..b43e149e9fe 100644 --- a/doc/classes/JavaClassWrapper.xml +++ b/doc/classes/JavaClassWrapper.xml @@ -6,6 +6,15 @@ The JavaClassWrapper singleton provides a way for the Godot application to send and receive data through the [url=https://developer.android.com/training/articles/perf-jni]Java Native Interface[/url] (JNI). [b]Note:[/b] This singleton is only available in Android builds. + [codeblock] + var LocalDateTime = JavaClassWrapper.wrap("java.time.LocalDateTime") + var DateTimeFormatter = JavaClassWrapper.wrap("java.time.format.DateTimeFormatter") + + var datetime = LocalDateTime.now() + var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss") + + print(datetime.format(formatter)) + [/codeblock] diff --git a/doc/classes/JavaObject.xml b/doc/classes/JavaObject.xml new file mode 100644 index 00000000000..f38070e7d9d --- /dev/null +++ b/doc/classes/JavaObject.xml @@ -0,0 +1,21 @@ + + + + Represents an object from the Java Native Interface. + + + Represents an object from the Java Native Interface. It can be returned from Java methods called on [JavaClass] or other [JavaObject]s. See [JavaClassWrapper] for an example. + [b]Note:[/b] This class only works on Android. On any other platform, this class does nothing. + [b]Note:[/b] This class is not to be confused with [JavaScriptObject]. + + + + + + + + Returns the [JavaClass] that this object is an instance of. + + + + diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp index 60c369951c9..6920f801e52 100644 --- a/platform/android/api/api.cpp +++ b/platform/android/api/api.cpp @@ -49,6 +49,7 @@ void register_android_api() { #endif GDREGISTER_CLASS(JavaClass); + GDREGISTER_CLASS(JavaObject); GDREGISTER_CLASS(JavaClassWrapper); Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", JavaClassWrapper::get_singleton())); } @@ -59,6 +60,16 @@ void unregister_android_api() { #endif } +void JavaClass::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_java_class_name"), &JavaClass::get_java_class_name); + ClassDB::bind_method(D_METHOD("get_java_method_list"), &JavaClass::get_java_method_list); + ClassDB::bind_method(D_METHOD("get_java_parent_class"), &JavaClass::get_java_parent_class); +} + +void JavaObject::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_java_class"), &JavaObject::get_java_class); +} + void JavaClassWrapper::_bind_methods() { ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap); } @@ -69,13 +80,32 @@ Variant JavaClass::callp(const StringName &, const Variant **, int, Callable::Ca return Variant(); } +String JavaClass::get_java_class_name() const { + return ""; +} + +TypedArray JavaClass::get_java_method_list() const { + return TypedArray(); +} + +Ref JavaClass::get_java_parent_class() const { + return Ref(); +} + JavaClass::JavaClass() { } +JavaClass::~JavaClass() { +} + Variant JavaObject::callp(const StringName &, const Variant **, int, Callable::CallError &) { return Variant(); } +Ref JavaObject::get_java_class() const { + return Ref(); +} + JavaClassWrapper *JavaClassWrapper::singleton = nullptr; Ref JavaClassWrapper::wrap(const String &) { diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h index e21a331ab9d..52df1644be8 100644 --- a/platform/android/api/java_class_wrapper.h +++ b/platform/android/api/java_class_wrapper.h @@ -32,6 +32,7 @@ #define JAVA_CLASS_WRAPPER_H #include "core/object/ref_counted.h" +#include "core/variant/typed_array.h" #ifdef ANDROID_ENABLED #include @@ -67,6 +68,7 @@ class JavaClass : public RefCounted { struct MethodInfo { bool _static = false; + bool _constructor = false; Vector param_types; Vector param_sigs; uint32_t return_type = 0; @@ -174,14 +176,29 @@ class JavaClass : public RefCounted { bool _call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret); friend class JavaClassWrapper; + friend class JavaObject; + String java_class_name; + String java_constructor_name; HashMap> methods; jclass _class; #endif +protected: + static void _bind_methods(); + public: virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; + String get_java_class_name() const; + TypedArray get_java_method_list() const; + Ref get_java_parent_class() const; + +#ifdef ANDROID_ENABLED + virtual String to_string() override; +#endif + JavaClass(); + ~JavaClass(); }; class JavaObject : public RefCounted { @@ -191,14 +208,24 @@ class JavaObject : public RefCounted { Ref base_class; friend class JavaClass; - jobject instance; + jobject instance = nullptr; #endif +protected: + static void _bind_methods(); + public: virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; + Ref get_java_class() const; + #ifdef ANDROID_ENABLED - JavaObject(const Ref &p_base, jobject *p_instance); + virtual String to_string() override; + + jobject get_instance() { return instance; } + + JavaObject(); + JavaObject(const Ref &p_base, jobject p_instance); ~JavaObject(); #endif }; @@ -209,13 +236,17 @@ class JavaClassWrapper : public Object { #ifdef ANDROID_ENABLED RBMap> class_cache; friend class JavaClass; - jmethodID getDeclaredMethods; - jmethodID getFields; - jmethodID getParameterTypes; - jmethodID getReturnType; - jmethodID getModifiers; - jmethodID getName; + jmethodID Class_getDeclaredConstructors; + jmethodID Class_getDeclaredMethods; + jmethodID Class_getFields; jmethodID Class_getName; + jmethodID Class_getSuperclass; + jmethodID Constructor_getParameterTypes; + jmethodID Constructor_getModifiers; + jmethodID Method_getParameterTypes; + jmethodID Method_getReturnType; + jmethodID Method_getModifiers; + jmethodID Method_getName; jmethodID Field_getName; jmethodID Field_getModifiers; jmethodID Field_get; @@ -242,6 +273,8 @@ public: Ref wrap(const String &p_class); #ifdef ANDROID_ENABLED + Ref wrap_jclass(jclass p_class); + JavaClassWrapper(jobject p_activity = nullptr); #else JavaClassWrapper(); diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 087fd1bacec..06afc4eb782 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -180,6 +180,11 @@ public: env->DeleteLocalRef(obj); } break; + case Variant::OBJECT: { + 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()); diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index a309a6ab740..f8553ca3978 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -44,7 +44,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, MethodInfo *method = nullptr; for (MethodInfo &E : M->value) { - if (!p_instance && !E._static) { + if (!p_instance && !E._static && !E._constructor) { r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; continue; } @@ -102,15 +102,19 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } } break; case ARG_TYPE_CLASS: { - if (p_args[i]->get_type() != Variant::OBJECT) { + if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) { arg_expected = Variant::OBJECT; } else { Ref ref = *p_args[i]; - if (!ref.is_null()) { + if (ref.is_valid()) { if (Object::cast_to(ref.ptr())) { Ref jo = ref; //could be faster - jclass c = env->FindClass(E.param_sigs[i].operator String().utf8().get_data()); + String cn = E.param_sigs[i].operator String(); + if (cn.begins_with("L") && cn.ends_with(";")) { + cn = cn.substr(1, cn.length() - 2); + } + jclass c = env->FindClass(cn.utf8().get_data()); if (!c || !env->IsInstanceOf(jo->instance, c)) { arg_expected = Variant::OBJECT; } else { @@ -458,7 +462,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; default: { jobject obj; - if (method->_static) { + if (method->_constructor) { + obj = env->NewObject(_class, method->method, argv); + } else if (method->_static) { obj = env->CallStaticObjectMethodA(_class, method->method, argv); } else { obj = env->CallObjectMethodA(p_instance->instance, method->method, argv); @@ -487,7 +493,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, Variant JavaClass::callp(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); + + String method = (p_method == java_constructor_name) ? "" : p_method; + bool found = _call_method(nullptr, method, p_args, p_argcount, r_error, ret); if (found) { return ret; } @@ -495,19 +503,156 @@ Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int return RefCounted::callp(p_method, p_args, p_argcount, r_error); } +String JavaClass::get_java_class_name() const { + return java_class_name; +} + +TypedArray JavaClass::get_java_method_list() const { + TypedArray method_list; + + for (const KeyValue> &item : methods) { + for (const MethodInfo &mi : item.value) { + Dictionary method; + + method["name"] = mi._constructor ? java_constructor_name : String(item.key); + method["id"] = (uint64_t)mi.method; + method["default_args"] = Array(); + method["flags"] = METHOD_FLAGS_DEFAULT & (mi._static || mi._constructor ? METHOD_FLAG_STATIC : METHOD_FLAG_NORMAL); + + { + Array a; + + for (uint32_t argtype : mi.param_types) { + Dictionary d; + + Variant::Type t = Variant::NIL; + float likelihood = 0.0; + _convert_to_variant_type(argtype, t, likelihood); + d["type"] = t; + if (t == Variant::OBJECT) { + d["hint"] = PROPERTY_HINT_RESOURCE_TYPE; + d["hint_string"] = "JavaObject"; + } else { + d["hint"] = 0; + d["hint_string"] = ""; + } + + a.push_back(d); + } + + method["args"] = a; + } + + { + Dictionary d; + + if (mi._constructor) { + d["type"] = Variant::OBJECT; + d["hint"] = PROPERTY_HINT_RESOURCE_TYPE; + d["hint_string"] = "JavaObject"; + } else { + Variant::Type t = Variant::NIL; + float likelihood = 0.0; + _convert_to_variant_type(mi.return_type, t, likelihood); + d["type"] = t; + if (t == Variant::OBJECT) { + d["hint"] = PROPERTY_HINT_RESOURCE_TYPE; + d["hint_string"] = "JavaObject"; + } else { + d["hint"] = 0; + d["hint_string"] = ""; + } + } + + method["return_type"] = d; + } + + method_list.push_back(method); + } + } + + return method_list; +} + +Ref JavaClass::get_java_parent_class() const { + ERR_FAIL_NULL_V(_class, Ref()); + + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, Ref()); + + jclass superclass = (jclass)env->CallObjectMethod(_class, JavaClassWrapper::singleton->Class_getSuperclass); + if (!superclass) { + return Ref(); + } + + Ref ret = JavaClassWrapper::singleton->wrap_jclass(superclass); + env->DeleteLocalRef(superclass); + return ret; +} + +String JavaClass::to_string() { + return ""; +} + JavaClass::JavaClass() { } +JavaClass::~JavaClass() { + if (_class) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + env->DeleteGlobalRef(_class); + } +} + ///////////////////// Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - return Variant(); + if (instance) { + Ref c = base_class; + while (c.is_valid()) { + Variant ret; + bool found = c->_call_method(this, p_method, p_args, p_argcount, r_error, ret); + if (found) { + return ret; + } + c = c->get_java_parent_class(); + } + } + + return RefCounted::callp(p_method, p_args, p_argcount, r_error); } -JavaObject::JavaObject(const Ref &p_base, jobject *p_instance) { +Ref JavaObject::get_java_class() const { + return base_class; +} + +String JavaObject::to_string() { + if (base_class.is_valid() && instance) { + return "java_class_name + " \"" + (String)call("toString") + "\">"; + } + return RefCounted::to_string(); +} + +JavaObject::JavaObject() { +} + +JavaObject::JavaObject(const Ref &p_base, jobject p_instance) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + base_class = p_base; + instance = env->NewGlobalRef(p_instance); } JavaObject::~JavaObject() { + if (instance) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + env->DeleteGlobalRef(instance); + } } //////////////////// @@ -649,6 +794,16 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va return true; } break; case ARG_TYPE_CLASS: { + jclass java_class = env->GetObjectClass(obj); + Ref java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class); + env->DeleteLocalRef(java_class); + + if (java_class_wrapped.is_valid()) { + Ref ret = Ref(memnew(JavaObject(java_class_wrapped, obj))); + var = ret; + return true; + } + return false; } break; case ARG_ARRAY_BIT | ARG_TYPE_VOID: { @@ -966,43 +1121,67 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va } Ref JavaClassWrapper::wrap(const String &p_class) { - if (class_cache.has(p_class)) { - return class_cache[p_class]; + String class_name_dots = p_class.replace("/", "."); + if (class_cache.has(class_name_dots)) { + return class_cache[class_name_dots]; } JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, Ref()); - jclass bclass = env->FindClass(p_class.utf8().get_data()); + jclass bclass = env->FindClass(class_name_dots.replace(".", "/").utf8().get_data()); ERR_FAIL_NULL_V(bclass, Ref()); - jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, getDeclaredMethods); + jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors); + ERR_FAIL_NULL_V(constructors, Ref()); + jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredMethods); ERR_FAIL_NULL_V(methods, Ref()); Ref java_class = memnew(JavaClass); + java_class->java_class_name = class_name_dots; + Vector class_name_parts = class_name_dots.split("."); + java_class->java_constructor_name = class_name_parts[class_name_parts.size() - 1]; + java_class->_class = (jclass)env->NewGlobalRef(bclass); + class_cache[class_name_dots] = java_class; - int count = env->GetArrayLength(methods); + LocalVector methods_and_constructors; + int constructor_count = env->GetArrayLength(constructors); + int method_count = env->GetArrayLength(methods); + methods_and_constructors.resize(method_count + constructor_count); + for (int i = 0; i < constructor_count; i++) { + methods_and_constructors[i] = env->GetObjectArrayElement(constructors, i); + } + for (int i = 0; i < method_count; i++) { + methods_and_constructors[constructor_count + i] = env->GetObjectArrayElement(methods, i); + } - for (int i = 0; i < count; i++) { - jobject obj = env->GetObjectArrayElement(methods, i); + for (int i = 0; i < (int)methods_and_constructors.size(); i++) { + jobject obj = methods_and_constructors[i]; ERR_CONTINUE(!obj); - jstring name = (jstring)env->CallObjectMethod(obj, getName); - String str_method = jstring_to_string(name, env); - env->DeleteLocalRef(name); + bool is_constructor = i < constructor_count; + + String str_method; + if (is_constructor) { + str_method = ""; + } else { + jstring name = (jstring)env->CallObjectMethod(obj, Method_getName); + str_method = jstring_to_string(name, env); + env->DeleteLocalRef(name); + } Vector params; - jint mods = env->CallIntMethod(obj, getModifiers); + jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers); if (!(mods & 0x0001)) { env->DeleteLocalRef(obj); continue; //not public bye } - jobjectArray param_types = (jobjectArray)env->CallObjectMethod(obj, getParameterTypes); - int count2 = env->GetArrayLength(param_types); + jobjectArray param_types = (jobjectArray)env->CallObjectMethod(obj, is_constructor ? Constructor_getParameterTypes : Method_getParameterTypes); + int count = env->GetArrayLength(param_types); if (!java_class->methods.has(str_method)) { java_class->methods[str_method] = List(); @@ -1010,10 +1189,11 @@ Ref JavaClassWrapper::wrap(const String &p_class) { JavaClass::MethodInfo mi; mi._static = (mods & 0x8) != 0; + mi._constructor = is_constructor; bool valid = true; String signature = "("; - for (int j = 0; j < count2; j++) { + for (int j = 0; j < count; j++) { jobject obj2 = env->GetObjectArrayElement(param_types, j); String strsig; uint32_t sig = 0; @@ -1029,7 +1209,7 @@ Ref JavaClassWrapper::wrap(const String &p_class) { } if (!valid) { - print_line("Method can't be bound (unsupported arguments): " + p_class + "::" + str_method); + print_line("Method can't be bound (unsupported arguments): " + class_name_dots + "::" + str_method); env->DeleteLocalRef(obj); env->DeleteLocalRef(param_types); continue; @@ -1037,21 +1217,28 @@ Ref JavaClassWrapper::wrap(const String &p_class) { signature += ")"; - jobject return_type = (jobject)env->CallObjectMethod(obj, getReturnType); + if (is_constructor) { + signature += "V"; + mi.return_type = JavaClass::ARG_TYPE_CLASS; + } else { + jobject return_type = (jobject)env->CallObjectMethod(obj, Method_getReturnType); + + String strsig; + uint32_t sig = 0; + if (!_get_type_sig(env, return_type, sig, strsig)) { + print_line("Method can't be bound (unsupported return type): " + class_name_dots + "::" + str_method); + env->DeleteLocalRef(obj); + env->DeleteLocalRef(param_types); + env->DeleteLocalRef(return_type); + continue; + } + + signature += strsig; + mi.return_type = sig; - String strsig; - uint32_t sig = 0; - if (!_get_type_sig(env, return_type, sig, strsig)) { - print_line("Method can't be bound (unsupported return type): " + p_class + "::" + str_method); - env->DeleteLocalRef(obj); - env->DeleteLocalRef(param_types); env->DeleteLocalRef(return_type); - continue; } - signature += strsig; - mi.return_type = sig; - bool discard = false; for (List::Element *E = java_class->methods[str_method].front(); E; E = E->next()) { @@ -1103,14 +1290,14 @@ Ref JavaClassWrapper::wrap(const String &p_class) { env->DeleteLocalRef(obj); env->DeleteLocalRef(param_types); - env->DeleteLocalRef(return_type); } + env->DeleteLocalRef(constructors); env->DeleteLocalRef(methods); - jobjectArray fields = (jobjectArray)env->CallObjectMethod(bclass, getFields); + jobjectArray fields = (jobjectArray)env->CallObjectMethod(bclass, Class_getFields); - count = env->GetArrayLength(fields); + int count = env->GetArrayLength(fields); for (int i = 0; i < count; i++) { jobject obj = env->GetObjectArrayElement(fields, i); @@ -1146,7 +1333,18 @@ Ref JavaClassWrapper::wrap(const String &p_class) { env->DeleteLocalRef(fields); - return Ref(); + return java_class; +} + +Ref JavaClassWrapper::wrap_jclass(jclass p_class) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, Ref()); + + jstring class_name = (jstring)env->CallObjectMethod(p_class, Class_getName); + String class_name_string = jstring_to_string(class_name, env); + env->DeleteLocalRef(class_name); + + return wrap(class_name_string); } JavaClassWrapper *JavaClassWrapper::singleton = nullptr; @@ -1158,16 +1356,23 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) { ERR_FAIL_NULL(env); jclass bclass = env->FindClass("java/lang/Class"); - getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); - getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;"); + Class_getDeclaredConstructors = env->GetMethodID(bclass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); + Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); + Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;"); Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;"); + Class_getSuperclass = env->GetMethodID(bclass, "getSuperclass", "()Ljava/lang/Class;"); + env->DeleteLocalRef(bclass); + + bclass = env->FindClass("java/lang/reflect/Constructor"); + Constructor_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;"); + Constructor_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); env->DeleteLocalRef(bclass); bclass = env->FindClass("java/lang/reflect/Method"); - getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;"); - getReturnType = env->GetMethodID(bclass, "getReturnType", "()Ljava/lang/Class;"); - getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;"); - getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); + Method_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;"); + Method_getReturnType = env->GetMethodID(bclass, "getReturnType", "()Ljava/lang/Class;"); + Method_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;"); + Method_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); env->DeleteLocalRef(bclass); bclass = env->FindClass("java/lang/reflect/Field"); diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index fc97d6eca44..4c17d03c602 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -30,6 +30,8 @@ #include "jni_utils.h" +#include "api/java_class_wrapper.h" + jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) { jvalret v; @@ -185,6 +187,16 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a v.obj = arr; } break; + case Variant::OBJECT: { + Ref generic_object = *p_arg; + if (generic_object.is_valid()) { + jobject obj = env->NewLocalRef(generic_object->get_instance()); + v.val.l = obj; + v.obj = obj; + } else { + v.val.i = 0; + } + } break; default: { v.val.i = 0; @@ -358,9 +370,11 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { return ret; } + Ref generic_object(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap(name), obj))); + env->DeleteLocalRef(c); - return Variant(); + return generic_object; } Variant::Type get_jni_type(const String &p_type) { @@ -395,10 +409,10 @@ Variant::Type get_jni_type(const String &p_type) { idx++; } - return Variant::NIL; + return Variant::OBJECT; } -const char *get_jni_sig(const String &p_type) { +String get_jni_sig(const String &p_type) { static struct { const char *name; const char *sig; @@ -430,5 +444,5 @@ const char *get_jni_sig(const String &p_type) { idx++; } - return "Ljava/lang/Object;"; + return "L" + p_type.replace(".", "/") + ";"; } diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h index c608f9ebaa2..631acd1cef5 100644 --- a/platform/android/jni_utils.h +++ b/platform/android/jni_utils.h @@ -52,6 +52,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj); Variant::Type get_jni_type(const String &p_type); -const char *get_jni_sig(const String &p_type); +String get_jni_sig(const String &p_type); #endif // JNI_UTILS_H