From b2c0a687f1cd8328556d9e0f36f21dcba05feb1a Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 13 Mar 2019 23:51:55 +1100 Subject: Restructuring glue code to make it easier to extend --- platform/android/SCsub | 4 +- platform/android/audio_driver_jandroid.h | 2 +- platform/android/dir_access_jandroid.h | 2 +- platform/android/file_access_jandroid.h | 2 +- .../java/src/org/godotengine/godot/Godot.java | 3 + .../java/src/org/godotengine/godot/GodotLib.java | 1 + platform/android/java_glue.cpp | 1589 -------------------- platform/android/java_glue.h | 66 - platform/android/java_godot_io_wrapper.cpp | 207 +++ platform/android/java_godot_io_wrapper.h | 88 ++ platform/android/java_godot_lib_jni.cpp | 1398 +++++++++++++++++ platform/android/java_godot_lib_jni.h | 69 + platform/android/java_godot_wrapper.cpp | 185 +++ platform/android/java_godot_wrapper.h | 81 + platform/android/os_android.cpp | 145 +- platform/android/os_android.h | 61 +- 16 files changed, 2110 insertions(+), 1793 deletions(-) delete mode 100644 platform/android/java_glue.cpp delete mode 100644 platform/android/java_glue.h create mode 100644 platform/android/java_godot_io_wrapper.cpp create mode 100644 platform/android/java_godot_io_wrapper.h create mode 100644 platform/android/java_godot_lib_jni.cpp create mode 100644 platform/android/java_godot_lib_jni.h create mode 100644 platform/android/java_godot_wrapper.cpp create mode 100644 platform/android/java_godot_wrapper.h diff --git a/platform/android/SCsub b/platform/android/SCsub index 47d5035224..80ee6644f3 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -16,8 +16,10 @@ android_files = [ 'dir_access_jandroid.cpp', 'thread_jandroid.cpp', 'audio_driver_jandroid.cpp', - 'java_glue.cpp', + 'java_godot_lib_jni.cpp', 'java_class_wrapper.cpp', + 'java_godot_wrapper.cpp', + 'java_godot_io_wrapper.cpp', # 'power_android.cpp' ] diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h index a5b49e9077..f92ef06052 100644 --- a/platform/android/audio_driver_jandroid.h +++ b/platform/android/audio_driver_jandroid.h @@ -33,7 +33,7 @@ #include "servers/audio_server.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" class AudioDriverAndroid : public AudioDriver { diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index e7a2d5ada1..cdea93ff4c 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -32,7 +32,7 @@ #define DIR_ACCESS_JANDROID_H #include "core/os/dir_access.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" #include class DirAccessJAndroid : public DirAccess { diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 304c33ecac..4f02fea81d 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -32,7 +32,7 @@ #define FILE_ACCESS_JANDROID_H #include "core/os/file_access.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" class FileAccessJAndroid : public FileAccess { static jobject io; diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index e42099ba0b..48fd076d31 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -606,6 +606,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC for (int i = 0; i < singleton_count; i++) { singletons[i].onMainDestroy(); } + + GodotLib.ondestroy(this); + super.onDestroy(); } diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java index 6e6b398a5d..31ca9a8500 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java @@ -46,6 +46,7 @@ public class GodotLib { */ public static native void initialize(Godot p_instance, Object p_asset_manager, boolean use_apk_expansion); + public static native void ondestroy(Godot p_instance); public static native void setup(String[] p_cmdline); public static native void resize(int width, int height); public static native void newcontext(boolean p_32_bits); diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp deleted file mode 100644 index e9c0e5564f..0000000000 --- a/platform/android/java_glue.cpp +++ /dev/null @@ -1,1589 +0,0 @@ -/*************************************************************************/ -/* java_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "java_glue.h" -#include "android/asset_manager_jni.h" -#include "audio_driver_jandroid.h" -#include "core/engine.h" -#include "core/os/keyboard.h" -#include "core/project_settings.h" -#include "dir_access_jandroid.h" -#include "file_access_android.h" -#include "file_access_jandroid.h" -#include "java_class_wrapper.h" -#include "main/input_default.h" -#include "main/main.h" -#include "os_android.h" -#include "string_android.h" -#include "thread_jandroid.h" -#include - -static JavaClassWrapper *java_class_wrapper = NULL; -static OS_Android *os_android = NULL; - -struct jvalret { - - jobject obj; - jvalue val; - jvalret() { obj = NULL; } -}; - -jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false) { - - jvalret v; - - switch (p_type) { - - case Variant::BOOL: { - - if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Boolean"); - jmethodID ctor = env->GetMethodID(bclass, "", "(Z)V"); - jvalue val; - val.z = (bool)(*p_arg); - jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; - env->DeleteLocalRef(bclass); - } else { - v.val.z = *p_arg; - }; - } break; - case Variant::INT: { - - if (force_jobject) { - - jclass bclass = env->FindClass("java/lang/Integer"); - jmethodID ctor = env->GetMethodID(bclass, "", "(I)V"); - jvalue val; - val.i = (int)(*p_arg); - jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; - env->DeleteLocalRef(bclass); - - } else { - v.val.i = *p_arg; - }; - } break; - case Variant::REAL: { - - if (force_jobject) { - - jclass bclass = env->FindClass("java/lang/Double"); - jmethodID ctor = env->GetMethodID(bclass, "", "(D)V"); - jvalue val; - val.d = (double)(*p_arg); - jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; - env->DeleteLocalRef(bclass); - - } else { - v.val.f = *p_arg; - }; - } 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::POOL_STRING_ARRAY: { - - PoolVector 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); - } - v.val.l = arr; - v.obj = arr; - - } break; - - case Variant::DICTIONARY: { - - Dictionary dict = *p_arg; - jclass dclass = env->FindClass("org/godotengine/godot/Dictionary"); - jmethodID ctor = env->GetMethodID(dclass, "", "()V"); - jobject jdict = env->NewObject(dclass, ctor); - - Array keys = dict.keys(); - - jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); - for (int j = 0; j < keys.size(); j++) { - jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data()); - env->SetObjectArrayElement(jkeys, j, str); - env->DeleteLocalRef(str); - }; - - jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V"); - jvalue val; - val.l = jkeys; - env->CallVoidMethodA(jdict, set_keys, &val); - env->DeleteLocalRef(jkeys); - - jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL); - - for (int j = 0; j < keys.size(); j++) { - Variant var = dict[keys[j]]; - jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true); - env->SetObjectArrayElement(jvalues, j, v.val.l); - if (v.obj) { - env->DeleteLocalRef(v.obj); - } - }; - - jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V"); - val.l = jvalues; - env->CallVoidMethodA(jdict, set_values, &val); - env->DeleteLocalRef(jvalues); - env->DeleteLocalRef(dclass); - - v.val.l = jdict; - v.obj = jdict; - } break; - - case Variant::POOL_INT_ARRAY: { - - PoolVector array = *p_arg; - jintArray arr = env->NewIntArray(array.size()); - PoolVector::Read r = array.read(); - env->SetIntArrayRegion(arr, 0, array.size(), r.ptr()); - v.val.l = arr; - v.obj = arr; - - } break; - case Variant::POOL_BYTE_ARRAY: { - PoolVector array = *p_arg; - jbyteArray arr = env->NewByteArray(array.size()); - PoolVector::Read r = array.read(); - env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast(r.ptr())); - v.val.l = arr; - v.obj = arr; - - } break; - case Variant::POOL_REAL_ARRAY: { - - PoolVector array = *p_arg; - jfloatArray arr = env->NewFloatArray(array.size()); - PoolVector::Read r = array.read(); - env->SetFloatArrayRegion(arr, 0, array.size(), r.ptr()); - v.val.l = arr; - v.obj = arr; - - } break; - default: { - - v.val.i = 0; - } break; - } - return v; -} - -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); - - if (array) { - jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z"); - jboolean isarr = env->CallBooleanMethod(cls, isArray); - (*array) = isarr ? true : false; - } - String name = jstring_to_string(clsName, env); - env->DeleteLocalRef(clsName); - - return name; -} - -Variant _jobject_to_variant(JNIEnv *env, jobject obj) { - - if (obj == NULL) { - return Variant(); - } - - jclass c = env->GetObjectClass(obj); - bool array; - 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); - PoolVector sarr; - - for (int i = 0; i < stringCount; i++) { - jstring string = (jstring)env->GetObjectArrayElement(arr, i); - sarr.push_back(jstring_to_string(string, env)); - env->DeleteLocalRef(string); - } - - return sarr; - }; - - 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); - return ret; - }; - - if (name == "[I") { - - jintArray arr = (jintArray)obj; - int fCount = env->GetArrayLength(arr); - PoolVector sarr; - sarr.resize(fCount); - - PoolVector::Write w = sarr.write(); - env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector::Write(); - return sarr; - }; - - if (name == "[B") { - - jbyteArray arr = (jbyteArray)obj; - int fCount = env->GetArrayLength(arr); - PoolVector sarr; - sarr.resize(fCount); - - PoolVector::Write w = sarr.write(); - env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast(w.ptr())); - w = PoolVector::Write(); - return sarr; - }; - - 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); - return ret; - }; - - if (name == "[D") { - - jdoubleArray arr = (jdoubleArray)obj; - int fCount = env->GetArrayLength(arr); - PoolRealArray sarr; - sarr.resize(fCount); - - PoolRealArray::Write w = sarr.write(); - - for (int i = 0; i < fCount; i++) { - - double n; - env->GetDoubleArrayRegion(arr, i, 1, &n); - w.ptr()[i] = n; - }; - return sarr; - }; - - if (name == "[F") { - - jfloatArray arr = (jfloatArray)obj; - int fCount = env->GetArrayLength(arr); - PoolRealArray sarr; - sarr.resize(fCount); - - PoolRealArray::Write w = sarr.write(); - - for (int i = 0; i < fCount; i++) { - - float n; - env->GetFloatArrayRegion(arr, i, 1, &n); - w.ptr()[i] = n; - }; - return sarr; - }; - - if (name == "[Ljava.lang.Object;") { - - jobjectArray arr = (jobjectArray)obj; - int objCount = env->GetArrayLength(arr); - Array varr; - - for (int i = 0; i < objCount; i++) { - jobject jobj = env->GetObjectArrayElement(arr, i); - Variant v = _jobject_to_variant(env, jobj); - varr.push_back(v); - env->DeleteLocalRef(jobj); - } - - return varr; - }; - - 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;"); - jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys); - - PoolStringArray keys = _jobject_to_variant(env, arr); - env->DeleteLocalRef(arr); - - jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;"); - arr = (jobjectArray)env->CallObjectMethod(obj, get_values); - - Array vals = _jobject_to_variant(env, arr); - env->DeleteLocalRef(arr); - - for (int i = 0; i < keys.size(); i++) { - - ret[keys[i]] = vals[i]; - }; - - return ret; - }; - - env->DeleteLocalRef(c); - - return Variant(); -} - -class JNISingleton : public Object { - - GDCLASS(JNISingleton, Object); - - struct MethodData { - - jmethodID method; - Variant::Type ret_type; - Vector argtypes; - }; - - jobject instance; - Map method_map; - -public: - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - - ERR_FAIL_COND_V(!instance, Variant()); - - r_error.error = Variant::CallError::CALL_OK; - - Map::Element *E = method_map.find(p_method); - if (!E) { - - r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - - int ac = E->get().argtypes.size(); - if (ac < p_argcount) { - - r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - if (ac > p_argcount) { - - r_error.error = Variant::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 = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = i; - r_error.expected = E->get().argtypes[i]; - } - } - - jvalue *v = NULL; - - 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 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::REAL: { - - 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::POOL_STRING_ARRAY: { - - jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); - - ret = _jobject_to_variant(env, arr); - - env->DeleteLocalRef(arr); - } break; - case Variant::POOL_INT_ARRAY: { - - jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - PoolVector sarr; - sarr.resize(fCount); - - PoolVector::Write w = sarr.write(); - env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector::Write(); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::POOL_REAL_ARRAY: { - - jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - PoolVector sarr; - sarr.resize(fCount); - - PoolVector::Write w = sarr.write(); - env->GetFloatArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector::Write(); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - - 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(NULL); - ERR_FAIL_V(Variant()); - } break; - } - - while (to_erase.size()) { - env->DeleteLocalRef(to_erase.front()->get()); - to_erase.pop_front(); - } - - env->PopLocalFrame(NULL); - - 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 &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 = NULL; - } -}; - -struct TST { - - int a; - TST() { - - a = 5; - } -}; - -TST tst; - -static bool initialized = false; -static int step = 0; -static Size2 new_size; -static Vector3 accelerometer; -static Vector3 gravity; -static Vector3 magnetometer; -static Vector3 gyroscope; -static HashMap jni_singletons; -static jobject godot_io; - -typedef void (*GFXInitFunc)(void *ud, bool gl2); - -static jmethodID _on_video_init = 0; -static jmethodID _restart = 0; -static jobject _godot_instance; - -static jmethodID _openURI = 0; -static jmethodID _getDataDir = 0; -static jmethodID _getLocale = 0; -static jmethodID _getClipboard = 0; -static jmethodID _setClipboard = 0; -static jmethodID _getModel = 0; -static jmethodID _getScreenDPI = 0; -static jmethodID _showKeyboard = 0; -static jmethodID _hideKeyboard = 0; -static jmethodID _setScreenOrientation = 0; -static jmethodID _getUniqueID = 0; -static jmethodID _getSystemDir = 0; -static jmethodID _getGLESVersionCode = 0; -static jmethodID _playVideo = 0; -static jmethodID _isVideoPlaying = 0; -static jmethodID _pauseVideo = 0; -static jmethodID _stopVideo = 0; -static jmethodID _setKeepScreenOn = 0; -static jmethodID _alertDialog = 0; -static jmethodID _requestPermission = 0; - -static void _gfx_init_func(void *ud, bool gl2) { -} - -static int _open_uri(const String &p_uri) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); - return env->CallIntMethod(godot_io, _openURI, jStr); -} - -static String _get_user_data_dir() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getDataDir); - return jstring_to_string(s, env); -} - -static String _get_locale() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getLocale); - return jstring_to_string(s, env); -} - -static String _get_clipboard() { - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(_godot_instance, _getClipboard); - return jstring_to_string(s, env); -} - -static void _set_clipboard(const String &p_text) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); - env->CallVoidMethod(_godot_instance, _setClipboard, jStr); -} - -static String _get_model() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getModel); - return jstring_to_string(s, env); -} - -static int _get_screen_dpi() { - - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallIntMethod(godot_io, _getScreenDPI); -} - -static String _get_unique_id() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getUniqueID); - return jstring_to_string(s, env); -} - -static void _show_vk(const String &p_existing) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); - env->CallVoidMethod(godot_io, _showKeyboard, jStr); -} - -static void _set_screen_orient(int p_orient) { - - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _setScreenOrientation, p_orient); -} - -static String _get_system_dir(int p_dir) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getSystemDir, p_dir); - return jstring_to_string(s, env); -} - -static int _get_gles_version_code() { - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallIntMethod(_godot_instance, _getGLESVersionCode); -} - -static void _hide_vk() { - - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _hideKeyboard); -} - -// virtual Error native_video_play(String p_path); -// virtual bool native_video_is_playing(); -// virtual void native_video_pause(); -// virtual void native_video_stop(); - -static void _play_video(const String &p_path) { -} - -static bool _is_video_playing() { - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallBooleanMethod(godot_io, _isVideoPlaying); - //return false; -} - -static void _pause_video() { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _pauseVideo); -} - -static void _stop_video() { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _stopVideo); -} - -static void _set_keep_screen_on(bool p_enabled) { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(_godot_instance, _setKeepScreenOn, p_enabled); -} - -static void _alert(const String &p_message, const String &p_title) { - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data()); - jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data()); - env->CallVoidMethod(_godot_instance, _alertDialog, jStrMessage, jStrTitle); -} - -static bool _request_permission(const String &p_name) { - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); - return env->CallBooleanMethod(_godot_instance, _requestPermission, jStrName); -} - -// volatile because it can be changed from non-main thread and we need to -// ensure the change is immediately visible to other threads. -static volatile int virtual_keyboard_height; - -static int _get_vk_height() { - return virtual_keyboard_height; -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height) { - virtual_keyboard_height = p_height; -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) { - - initialized = true; - - JavaVM *jvm; - env->GetJavaVM(&jvm); - - _godot_instance = env->NewGlobalRef(activity); - //_godot_instance=activity; - - { - //setup IO Object - - 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;"); - jobject ob = env->GetStaticObjectField(cls, fid); - jobject gob = env->NewGlobalRef(ob); - - godot_io = gob; - - _on_video_init = env->GetMethodID(cls, "onVideoInit", "()V"); - _restart = env->GetMethodID(cls, "restart", "()V"); - _setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); - _alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); - _getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I"); - _getClipboard = env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); - _setClipboard = env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); - _requestPermission = env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); - - if (cls) { - jclass c = env->GetObjectClass(gob); - _openURI = env->GetMethodID(c, "openURI", "(Ljava/lang/String;)I"); - _getDataDir = env->GetMethodID(c, "getDataDir", "()Ljava/lang/String;"); - _getLocale = env->GetMethodID(c, "getLocale", "()Ljava/lang/String;"); - _getModel = env->GetMethodID(c, "getModel", "()Ljava/lang/String;"); - _getScreenDPI = env->GetMethodID(c, "getScreenDPI", "()I"); - _getUniqueID = env->GetMethodID(c, "getUniqueID", "()Ljava/lang/String;"); - _showKeyboard = env->GetMethodID(c, "showKeyboard", "(Ljava/lang/String;)V"); - _hideKeyboard = env->GetMethodID(c, "hideKeyboard", "()V"); - _setScreenOrientation = env->GetMethodID(c, "setScreenOrientation", "(I)V"); - _getSystemDir = env->GetMethodID(c, "getSystemDir", "(I)Ljava/lang/String;"); - _playVideo = env->GetMethodID(c, "playVideo", "(Ljava/lang/String;)V"); - _isVideoPlaying = env->GetMethodID(c, "isVideoPlaying", "()Z"); - _pauseVideo = env->GetMethodID(c, "pauseVideo", "()V"); - _stopVideo = env->GetMethodID(c, "stopVideo", "()V"); - } - - ThreadAndroid::make_default(jvm); -#ifdef USE_JAVA_FILE_ACCESS - FileAccessJAndroid::setup(gob); -#else - - jobject amgr = env->NewGlobalRef(p_asset_manager); - - FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr); -#endif - DirAccessJAndroid::setup(gob); - AudioDriverAndroid::setup(gob); - } - - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, _set_clipboard, _get_clipboard, _request_permission, p_use_apk_expansion); - - char wd[500]; - getcwd(wd, 500); - - env->CallVoidMethod(_godot_instance, _on_video_init); -} - -static void _initialize_java_modules() { - - if (!ProjectSettings::get_singleton()->has_setting("android/modules")) { - return; - } - - String modules = ProjectSettings::get_singleton()->get("android/modules"); - modules = modules.strip_edges(); - if (modules == String()) { - return; - } - Vector mods = modules.split(",", false); - - if (mods.size()) { - - JNIEnv *env = ThreadAndroid::get_env(); - - jclass activityClass = env->FindClass("org/godotengine/godot/Godot"); - - jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); - - jobject cls = env->CallObjectMethod(_godot_instance, getClassLoader); - - jclass classLoader = env->FindClass("java/lang/ClassLoader"); - - jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - - for (int i = 0; i < mods.size(); i++) { - - String m = mods[i]; - - print_line("Loading Android module: " + m); - jstring strClassName = env->NewStringUTF(m.utf8().get_data()); - jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName); - - if (!singletonClass) { - - ERR_EXPLAIN("Couldn't find singleton for class: " + m); - ERR_CONTINUE(!singletonClass); - } - - jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lorg/godotengine/godot/Godot$SingletonBase;"); - - if (!initialize) { - - ERR_EXPLAIN("Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: " + m); - ERR_CONTINUE(!initialize); - } - jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, _godot_instance); - env->NewGlobalRef(obj); - } - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) { - ThreadAndroid::setup_thread(); - - const char **cmdline = NULL; - jstring *j_cmdline = NULL; - int cmdlen = 0; - if (p_cmdline) { - cmdlen = env->GetArrayLength(p_cmdline); - if (cmdlen) { - cmdline = (const char **)malloc((cmdlen + 1) * sizeof(const char *)); - cmdline[cmdlen] = NULL; - 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); - - cmdline[i] = rawString; - j_cmdline[i] = string; - } - } - } - - Error err = Main::setup("apk", cmdlen, (char **)cmdline, false); - if (cmdline) { - if (j_cmdline) { - for (int i = 0; i < cmdlen; ++i) { - env->ReleaseStringUTFChars(j_cmdline[i], cmdline[i]); - } - free(j_cmdline); - } - free(cmdline); - } - - if (err != OK) { - return; //should exit instead and print the error - } - - java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); - Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", java_class_wrapper)); - _initialize_java_modules(); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height) { - - if (os_android) - os_android->set_display_size(Size2(width, height)); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { - - if (os_android) { - if (step == 0) { - // During startup - os_android->set_context_is_16_bits(!p_32_bits); - } else { - // GL context recreated because it was lost; restart app to let it reload everything - os_android->main_loop_end(); - env->CallVoidMethod(_godot_instance, _restart); - step = -1; // Ensure no further steps are attempted - } - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { - if (step == 0) - return; - - os_android->main_loop_request_go_back(); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { - if (step == -1) - 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()); - ++step; - return; - } - - if (step == 1) { - if (!Main::start()) { - return; //should exit instead and print the error - } - - os_android->main_loop_begin(); - ++step; - } - - os_android->process_accelerometer(accelerometer); - - os_android->process_gravity(gravity); - - os_android->process_magnetometer(magnetometer); - - os_android->process_gyroscope(gyroscope); - - if (os_android->main_loop_iterate()) { - - jclass cls = env->FindClass("org/godotengine/godot/Godot"); - jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); - env->CallVoidMethod(_godot_instance, _finish); - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { - - if (step == 0) - return; - - Vector points; - for (int i = 0; i < count; i++) { - - jint p[3]; - env->GetIntArrayRegion(positions, i * 3, 3, p); - OS_Android::TouchPos tp; - tp.pos = Point2(p[1], p[2]); - tp.id = p[0]; - points.push_back(tp); - } - - os_android->process_touch(ev, pointer, points); - - /* - if (os_android) - os_android->process_touch(ev,pointer,points); - */ -} - -/* - * Android Key codes. - */ -enum { - AKEYCODE_UNKNOWN = 0, - AKEYCODE_SOFT_LEFT = 1, - AKEYCODE_SOFT_RIGHT = 2, - AKEYCODE_HOME = 3, - AKEYCODE_BACK = 4, - AKEYCODE_CALL = 5, - AKEYCODE_ENDCALL = 6, - AKEYCODE_0 = 7, - AKEYCODE_1 = 8, - AKEYCODE_2 = 9, - AKEYCODE_3 = 10, - AKEYCODE_4 = 11, - AKEYCODE_5 = 12, - AKEYCODE_6 = 13, - AKEYCODE_7 = 14, - AKEYCODE_8 = 15, - AKEYCODE_9 = 16, - AKEYCODE_STAR = 17, - AKEYCODE_POUND = 18, - AKEYCODE_DPAD_UP = 19, - AKEYCODE_DPAD_DOWN = 20, - AKEYCODE_DPAD_LEFT = 21, - AKEYCODE_DPAD_RIGHT = 22, - AKEYCODE_DPAD_CENTER = 23, - AKEYCODE_VOLUME_UP = 24, - AKEYCODE_VOLUME_DOWN = 25, - AKEYCODE_POWER = 26, - AKEYCODE_CAMERA = 27, - AKEYCODE_CLEAR = 28, - AKEYCODE_A = 29, - AKEYCODE_B = 30, - AKEYCODE_C = 31, - AKEYCODE_D = 32, - AKEYCODE_E = 33, - AKEYCODE_F = 34, - AKEYCODE_G = 35, - AKEYCODE_H = 36, - AKEYCODE_I = 37, - AKEYCODE_J = 38, - AKEYCODE_K = 39, - AKEYCODE_L = 40, - AKEYCODE_M = 41, - AKEYCODE_N = 42, - AKEYCODE_O = 43, - AKEYCODE_P = 44, - AKEYCODE_Q = 45, - AKEYCODE_R = 46, - AKEYCODE_S = 47, - AKEYCODE_T = 48, - AKEYCODE_U = 49, - AKEYCODE_V = 50, - AKEYCODE_W = 51, - AKEYCODE_X = 52, - AKEYCODE_Y = 53, - AKEYCODE_Z = 54, - AKEYCODE_COMMA = 55, - AKEYCODE_PERIOD = 56, - AKEYCODE_ALT_LEFT = 57, - AKEYCODE_ALT_RIGHT = 58, - AKEYCODE_SHIFT_LEFT = 59, - AKEYCODE_SHIFT_RIGHT = 60, - AKEYCODE_TAB = 61, - AKEYCODE_SPACE = 62, - AKEYCODE_SYM = 63, - AKEYCODE_EXPLORER = 64, - AKEYCODE_ENVELOPE = 65, - AKEYCODE_ENTER = 66, - AKEYCODE_DEL = 67, - AKEYCODE_GRAVE = 68, - AKEYCODE_MINUS = 69, - AKEYCODE_EQUALS = 70, - AKEYCODE_LEFT_BRACKET = 71, - AKEYCODE_RIGHT_BRACKET = 72, - AKEYCODE_BACKSLASH = 73, - AKEYCODE_SEMICOLON = 74, - AKEYCODE_APOSTROPHE = 75, - AKEYCODE_SLASH = 76, - AKEYCODE_AT = 77, - AKEYCODE_NUM = 78, - AKEYCODE_HEADSETHOOK = 79, - AKEYCODE_FOCUS = 80, // *Camera* focus - AKEYCODE_PLUS = 81, - AKEYCODE_MENU = 82, - AKEYCODE_NOTIFICATION = 83, - AKEYCODE_SEARCH = 84, - AKEYCODE_MEDIA_PLAY_PAUSE = 85, - AKEYCODE_MEDIA_STOP = 86, - AKEYCODE_MEDIA_NEXT = 87, - AKEYCODE_MEDIA_PREVIOUS = 88, - AKEYCODE_MEDIA_REWIND = 89, - AKEYCODE_MEDIA_FAST_FORWARD = 90, - AKEYCODE_MUTE = 91, - AKEYCODE_PAGE_UP = 92, - AKEYCODE_PAGE_DOWN = 93, - AKEYCODE_PICTSYMBOLS = 94, - AKEYCODE_SWITCH_CHARSET = 95, - AKEYCODE_BUTTON_A = 96, - AKEYCODE_BUTTON_B = 97, - AKEYCODE_BUTTON_C = 98, - AKEYCODE_BUTTON_X = 99, - AKEYCODE_BUTTON_Y = 100, - AKEYCODE_BUTTON_Z = 101, - AKEYCODE_BUTTON_L1 = 102, - AKEYCODE_BUTTON_R1 = 103, - AKEYCODE_BUTTON_L2 = 104, - AKEYCODE_BUTTON_R2 = 105, - AKEYCODE_BUTTON_THUMBL = 106, - AKEYCODE_BUTTON_THUMBR = 107, - AKEYCODE_BUTTON_START = 108, - AKEYCODE_BUTTON_SELECT = 109, - AKEYCODE_BUTTON_MODE = 110, - - // NOTE: If you add a new keycode here you must also add it to several other files. - // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. -}; - -struct _WinTranslatePair { - - unsigned int keysym; - unsigned int keycode; -}; - -static _WinTranslatePair _ak_to_keycode[] = { - { KEY_TAB, AKEYCODE_TAB }, - { KEY_ENTER, AKEYCODE_ENTER }, - { KEY_SHIFT, AKEYCODE_SHIFT_LEFT }, - { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT }, - { KEY_ALT, AKEYCODE_ALT_LEFT }, - { KEY_ALT, AKEYCODE_ALT_RIGHT }, - { KEY_MENU, AKEYCODE_MENU }, - { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE }, - { KEY_ESCAPE, AKEYCODE_BACK }, - { KEY_SPACE, AKEYCODE_SPACE }, - { KEY_PAGEUP, AKEYCODE_PAGE_UP }, - { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN }, - { KEY_HOME, AKEYCODE_HOME }, //(0x24) - { KEY_LEFT, AKEYCODE_DPAD_LEFT }, - { KEY_UP, AKEYCODE_DPAD_UP }, - { KEY_RIGHT, AKEYCODE_DPAD_RIGHT }, - { KEY_DOWN, AKEYCODE_DPAD_DOWN }, - { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER }, - { KEY_BACKSPACE, AKEYCODE_DEL }, - { KEY_0, AKEYCODE_0 }, ////0 key - { KEY_1, AKEYCODE_1 }, ////1 key - { KEY_2, AKEYCODE_2 }, ////2 key - { KEY_3, AKEYCODE_3 }, ////3 key - { KEY_4, AKEYCODE_4 }, ////4 key - { KEY_5, AKEYCODE_5 }, ////5 key - { KEY_6, AKEYCODE_6 }, ////6 key - { KEY_7, AKEYCODE_7 }, ////7 key - { KEY_8, AKEYCODE_8 }, ////8 key - { KEY_9, AKEYCODE_9 }, ////9 key - { KEY_A, AKEYCODE_A }, ////A key - { KEY_B, AKEYCODE_B }, ////B key - { KEY_C, AKEYCODE_C }, ////C key - { KEY_D, AKEYCODE_D }, ////D key - { KEY_E, AKEYCODE_E }, ////E key - { KEY_F, AKEYCODE_F }, ////F key - { KEY_G, AKEYCODE_G }, ////G key - { KEY_H, AKEYCODE_H }, ////H key - { KEY_I, AKEYCODE_I }, ////I key - { KEY_J, AKEYCODE_J }, ////J key - { KEY_K, AKEYCODE_K }, ////K key - { KEY_L, AKEYCODE_L }, ////L key - { KEY_M, AKEYCODE_M }, ////M key - { KEY_N, AKEYCODE_N }, ////N key - { KEY_O, AKEYCODE_O }, ////O key - { KEY_P, AKEYCODE_P }, ////P key - { KEY_Q, AKEYCODE_Q }, ////Q key - { KEY_R, AKEYCODE_R }, ////R key - { KEY_S, AKEYCODE_S }, ////S key - { KEY_T, AKEYCODE_T }, ////T key - { KEY_U, AKEYCODE_U }, ////U key - { KEY_V, AKEYCODE_V }, ////V key - { KEY_W, AKEYCODE_W }, ////W key - { KEY_X, AKEYCODE_X }, ////X key - { KEY_Y, AKEYCODE_Y }, ////Y key - { KEY_Z, AKEYCODE_Z }, ////Z key - { KEY_HOMEPAGE, AKEYCODE_EXPLORER }, - { KEY_LAUNCH0, AKEYCODE_BUTTON_A }, - { KEY_LAUNCH1, AKEYCODE_BUTTON_B }, - { KEY_LAUNCH2, AKEYCODE_BUTTON_C }, - { KEY_LAUNCH3, AKEYCODE_BUTTON_X }, - { KEY_LAUNCH4, AKEYCODE_BUTTON_Y }, - { KEY_LAUNCH5, AKEYCODE_BUTTON_Z }, - { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 }, - { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 }, - { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 }, - { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 }, - { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL }, - { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR }, - { KEY_LAUNCHC, AKEYCODE_BUTTON_START }, - { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT }, - { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE }, - { KEY_VOLUMEMUTE, AKEYCODE_MUTE }, - { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN }, - { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP }, - { KEY_BACK, AKEYCODE_MEDIA_REWIND }, - { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD }, - { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT }, - { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS }, - { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP }, - { KEY_PLUS, AKEYCODE_PLUS }, - { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key - { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key - { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key - { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key - { KEY_BACKSLASH, AKEYCODE_BACKSLASH }, - { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, - { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, - { KEY_UNKNOWN, 0 } -}; -/* -TODO: map these android key: - AKEYCODE_SOFT_LEFT = 1, - AKEYCODE_SOFT_RIGHT = 2, - AKEYCODE_CALL = 5, - AKEYCODE_ENDCALL = 6, - AKEYCODE_STAR = 17, - AKEYCODE_POUND = 18, - AKEYCODE_POWER = 26, - AKEYCODE_CAMERA = 27, - AKEYCODE_CLEAR = 28, - AKEYCODE_SYM = 63, - AKEYCODE_ENVELOPE = 65, - AKEYCODE_GRAVE = 68, - AKEYCODE_SEMICOLON = 74, - AKEYCODE_APOSTROPHE = 75, - AKEYCODE_AT = 77, - AKEYCODE_NUM = 78, - AKEYCODE_HEADSETHOOK = 79, - AKEYCODE_FOCUS = 80, // *Camera* focus - AKEYCODE_NOTIFICATION = 83, - AKEYCODE_SEARCH = 84, - AKEYCODE_PICTSYMBOLS = 94, - AKEYCODE_SWITCH_CHARSET = 95, -*/ - -static 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; - } - } - - return KEY_UNKNOWN; -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) { - if (step == 0) - return; - - OS_Android::JoypadEvent jevent; - jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_BUTTON; - jevent.index = p_button; - jevent.pressed = p_pressed; - - os_android->process_joy_event(jevent); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { - if (step == 0) - return; - - OS_Android::JoypadEvent jevent; - jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_AXIS; - jevent.index = p_axis; - jevent.value = p_value; - - os_android->process_joy_event(jevent); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { - if (step == 0) - return; - - OS_Android::JoypadEvent jevent; - jevent.device = p_device; - jevent.type = OS_Android::JOY_EVENT_HAT; - int hat = 0; - if (p_hat_x != 0) { - if (p_hat_x < 0) - hat |= InputDefault::HAT_MASK_LEFT; - else - hat |= InputDefault::HAT_MASK_RIGHT; - } - if (p_hat_y != 0) { - if (p_hat_y < 0) - hat |= InputDefault::HAT_MASK_UP; - else - hat |= InputDefault::HAT_MASK_DOWN; - } - jevent.hat = hat; - - os_android->process_joy_event(jevent); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, 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); - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { - if (step == 0) - return; - - Ref ievent; - ievent.instance(); - int val = p_unicode_char; - int scancode = android_get_keysym(p_scancode); - ievent->set_scancode(scancode); - ievent->set_unicode(val); - ievent->set_pressed(p_pressed); - - if (val == '\n') { - ievent->set_scancode(KEY_ENTER); - } else if (val == 61448) { - ievent->set_scancode(KEY_BACKSPACE); - ievent->set_unicode(KEY_BACKSPACE); - } else if (val == 61453) { - ievent->set_scancode(KEY_ENTER); - ievent->set_unicode(KEY_ENTER); - } else if (p_scancode == 4) { - - os_android->main_loop_request_go_back(); - } - - os_android->process_event(ievent); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - accelerometer = Vector3(x, y, z); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - gravity = Vector3(x, y, z); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - magnetometer = Vector3(x, y, z); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - gyroscope = Vector3(x, y, z); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { - - if (step == 0) - return; - - os_android->main_loop_focusin(); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { - - if (step == 0) - return; - - os_android->main_loop_focusout(); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { - - ThreadAndroid::setup_thread(); - AudioDriverAndroid::thread_func(env); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object) { - - String singname = jstring_to_string(name, env); - JNISingleton *s = memnew(JNISingleton); - s->set_instance(env->NewGlobalRef(p_object)); - jni_singletons[singname] = s; - - Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); - ProjectSettings::get_singleton()->set(singname, s); -} - -static Variant::Type get_jni_type(const String &p_type) { - - static struct { - const char *name; - Variant::Type type; - } _type_to_vtype[] = { - { "void", Variant::NIL }, - { "boolean", Variant::BOOL }, - { "int", Variant::INT }, - { "float", Variant::REAL }, - { "double", Variant::REAL }, - { "java.lang.String", Variant::STRING }, - { "[I", Variant::POOL_INT_ARRAY }, - { "[B", Variant::POOL_BYTE_ARRAY }, - { "[F", Variant::POOL_REAL_ARRAY }, - { "[Ljava.lang.String;", Variant::POOL_STRING_ARRAY }, - { "org.godotengine.godot.Dictionary", Variant::DICTIONARY }, - { NULL, Variant::NIL } - }; - - int idx = 0; - - while (_type_to_vtype[idx].name) { - - if (p_type == _type_to_vtype[idx].name) - return _type_to_vtype[idx].type; - - idx++; - } - - return Variant::NIL; -} - -static const char *get_jni_sig(const String &p_type) { - - static struct { - const char *name; - const char *sig; - } _type_to_vtype[] = { - { "void", "V" }, - { "boolean", "Z" }, - { "int", "I" }, - { "float", "F" }, - { "double", "D" }, - { "java.lang.String", "Ljava/lang/String;" }, - { "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" }, - { "[I", "[I" }, - { "[B", "[B" }, - { "[F", "[F" }, - { "[Ljava.lang.String;", "[Ljava/lang/String;" }, - { NULL, "V" } - }; - - int idx = 0; - - while (_type_to_vtype[idx].name) { - - if (p_type == _type_to_vtype[idx].name) - return _type_to_vtype[idx].sig; - - idx++; - } - - return "Ljava/lang/Object;"; -} - -JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, 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_method(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)); - - JNISingleton *s = jni_singletons.get(singname); - - String mname = jstring_to_string(name, env); - String retval = jstring_to_string(ret, env); - Vector types; - String cs = "("; - - 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)); - cs += get_jni_sig(rawString); - } - - cs += ")"; - cs += get_jni_sig(retval); - 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_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) { - - Object *obj = ObjectDB::get_instance(ID); - ERR_FAIL_COND(!obj); - - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - - String str_method = jstring_to_string(method, env); - - int count = env->GetArrayLength(params); - 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) - v = _jobject_to_variant(env, obj); - memnew_placement(&vlist[i], Variant); - vlist[i] = v; - vptr[i] = &vlist[i]; - env->DeleteLocalRef(obj); - }; - - Variant::CallError err; - obj->call(str_method, (const Variant **)vptr, count, err); - // something - - env->PopLocalFrame(NULL); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) { - - Object *obj = ObjectDB::get_instance(ID); - ERR_FAIL_COND(!obj); - - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - - String str_method = jstring_to_string(method, env); - - int count = env->GetArrayLength(params); - 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); - env->DeleteLocalRef(obj); - }; - - obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4]); - // something - env->PopLocalFrame(NULL); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result) { - String permission = jstring_to_string(p_permission, env); - if (permission == "android.permission.RECORD_AUDIO" && p_result) { - AudioDriver::get_singleton()->capture_start(); - } -} diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h deleted file mode 100644 index 3b93c9b42a..0000000000 --- a/platform/android/java_glue.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************/ -/* java_glue.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 JAVA_GLUE_H -#define JAVA_GLUE_H - -#include -#include - -extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); -JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result); -} - -#endif // JAVA_GLUE_H diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp new file mode 100644 index 0000000000..0c41b85939 --- /dev/null +++ b/platform/android/java_godot_io_wrapper.cpp @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* java_godot_io_wrapper.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "java_godot_io_wrapper.h" +#include "core/error_list.h" + +// JNIEnv is only valid within the thread it belongs to, in a multi threading environment +// we can't cache it. +// For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv +// from ThreadAndroid. + +GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) { + godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance); + if (godot_io_instance) { + cls = p_env->GetObjectClass(godot_io_instance); + if (cls) { + cls = (jclass)p_env->NewGlobalRef(cls); + } else { + // this is a pretty serious fail.. bail... pointers will stay 0 + return; + } + + _open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I"); + _get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;"); + _get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;"); + _get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;"); + _get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I"); + _get_unique_ID = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;)V"); + _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); + _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); + _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"); + } +} + +GodotIOJavaWrapper::~GodotIOJavaWrapper() { + // nothing to do here for now +} + +jobject GodotIOJavaWrapper::get_instance() { + return godot_io_instance; +} + +Error GodotIOJavaWrapper::open_uri(const String &p_uri) { + if (_open_URI) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); + return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK; + } else { + return ERR_UNAVAILABLE; + } +} + +String GodotIOJavaWrapper::get_user_data_dir() { + if (_get_data_dir) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +String GodotIOJavaWrapper::get_locale() { + if (_get_locale) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +String GodotIOJavaWrapper::get_model() { + if (_get_model) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +int GodotIOJavaWrapper::get_screen_dpi() { + if (_get_screen_DPI) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallIntMethod(godot_io_instance, _get_screen_DPI); + } else { + return 160; + } +} + +String GodotIOJavaWrapper::get_unique_id() { + if (_get_unique_ID) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_ID); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +bool GodotIOJavaWrapper::has_vk() { + return (_show_keyboard != 0) && (_hide_keyboard != 0); +} + +void GodotIOJavaWrapper::show_vk(const String &p_existing) { + if (_show_keyboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr); + } +} + +void GodotIOJavaWrapper::hide_vk() { + if (_hide_keyboard) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _hide_keyboard); + } +} + +void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { + if (_set_screen_orientation) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient); + } +} + +String GodotIOJavaWrapper::get_system_dir(int p_dir) { + if (_get_system_dir) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir); + return jstring_to_string(s, env); + } else { + return String("."); + } +} + +void GodotIOJavaWrapper::play_video(const String &p_path) { + // Why is this not here?!?! +} + +bool GodotIOJavaWrapper::is_video_playing() { + if (_is_video_playing) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallBooleanMethod(godot_io_instance, _is_video_playing); + } 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); + } +} + +// volatile because it can be changed from non-main thread and we need to +// ensure the change is immediately visible to other threads. +static volatile int virtual_keyboard_height; + +int GodotIOJavaWrapper::get_vk_height() { + return virtual_keyboard_height; +} + +void GodotIOJavaWrapper::set_vk_height(int p_height) { + virtual_keyboard_height = p_height; +} diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h new file mode 100644 index 0000000000..920c433b08 --- /dev/null +++ b/platform/android/java_godot_io_wrapper.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* java_godot_io_wrapper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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. */ +/*************************************************************************/ + +// note, swapped java and godot around in the file name so all the java +// wrappers are together + +#ifndef JAVA_GODOT_IO_WRAPPER_H +#define JAVA_GODOT_IO_WRAPPER_H + +#include +#include + +#include "string_android.h" + +// Class that makes functions in java/src/org/godotengine/godot/GodotIO.java callable from C++ +class GodotIOJavaWrapper { +private: + jobject godot_io_instance; + jclass cls; + + jmethodID _open_URI = 0; + jmethodID _get_data_dir = 0; + jmethodID _get_locale = 0; + jmethodID _get_model = 0; + jmethodID _get_screen_DPI = 0; + jmethodID _get_unique_ID = 0; + jmethodID _show_keyboard = 0; + jmethodID _hide_keyboard = 0; + jmethodID _set_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); + ~GodotIOJavaWrapper(); + + jobject get_instance(); + + Error open_uri(const String &p_uri); + String get_user_data_dir(); + String get_locale(); + String get_model(); + int get_screen_dpi(); + String get_unique_id(); + bool has_vk(); + void show_vk(const String &p_existing); + void hide_vk(); + int get_vk_height(); + void set_vk_height(int p_height); + void set_screen_orientation(int p_orient); + 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 new file mode 100644 index 0000000000..466f79c215 --- /dev/null +++ b/platform/android/java_godot_lib_jni.cpp @@ -0,0 +1,1398 @@ +/*************************************************************************/ +/* java_godot_lib_jni.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "java_godot_lib_jni.h" +#include "java_godot_io_wrapper.h" +#include "java_godot_wrapper.h" + +#include "android/asset_manager_jni.h" +#include "audio_driver_jandroid.h" +#include "core/engine.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" +#include "dir_access_jandroid.h" +#include "file_access_android.h" +#include "file_access_jandroid.h" +#include "java_class_wrapper.h" +#include "main/input_default.h" +#include "main/main.h" +#include "os_android.h" +#include "string_android.h" +#include "thread_jandroid.h" +#include + +static JavaClassWrapper *java_class_wrapper = NULL; +static OS_Android *os_android = NULL; +static GodotJavaWrapper *godot_java = NULL; +static GodotIOJavaWrapper *godot_io_java = NULL; + +struct jvalret { + + jobject obj; + jvalue val; + jvalret() { obj = NULL; } +}; + +jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false) { + + jvalret v; + + switch (p_type) { + + case Variant::BOOL: { + + if (force_jobject) { + jclass bclass = env->FindClass("java/lang/Boolean"); + jmethodID ctor = env->GetMethodID(bclass, "", "(Z)V"); + jvalue val; + val.z = (bool)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.val.l = obj; + v.obj = obj; + env->DeleteLocalRef(bclass); + } else { + v.val.z = *p_arg; + }; + } break; + case Variant::INT: { + + if (force_jobject) { + + jclass bclass = env->FindClass("java/lang/Integer"); + jmethodID ctor = env->GetMethodID(bclass, "", "(I)V"); + jvalue val; + val.i = (int)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.val.l = obj; + v.obj = obj; + env->DeleteLocalRef(bclass); + + } else { + v.val.i = *p_arg; + }; + } break; + case Variant::REAL: { + + if (force_jobject) { + + jclass bclass = env->FindClass("java/lang/Double"); + jmethodID ctor = env->GetMethodID(bclass, "", "(D)V"); + jvalue val; + val.d = (double)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.val.l = obj; + v.obj = obj; + env->DeleteLocalRef(bclass); + + } else { + v.val.f = *p_arg; + }; + } 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::POOL_STRING_ARRAY: { + + PoolVector 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); + } + v.val.l = arr; + v.obj = arr; + + } break; + + case Variant::DICTIONARY: { + + Dictionary dict = *p_arg; + jclass dclass = env->FindClass("org/godotengine/godot/Dictionary"); + jmethodID ctor = env->GetMethodID(dclass, "", "()V"); + jobject jdict = env->NewObject(dclass, ctor); + + Array keys = dict.keys(); + + jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); + for (int j = 0; j < keys.size(); j++) { + jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data()); + env->SetObjectArrayElement(jkeys, j, str); + env->DeleteLocalRef(str); + }; + + jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V"); + jvalue val; + val.l = jkeys; + env->CallVoidMethodA(jdict, set_keys, &val); + env->DeleteLocalRef(jkeys); + + jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL); + + for (int j = 0; j < keys.size(); j++) { + Variant var = dict[keys[j]]; + jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true); + env->SetObjectArrayElement(jvalues, j, v.val.l); + if (v.obj) { + env->DeleteLocalRef(v.obj); + } + }; + + jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V"); + val.l = jvalues; + env->CallVoidMethodA(jdict, set_values, &val); + env->DeleteLocalRef(jvalues); + env->DeleteLocalRef(dclass); + + v.val.l = jdict; + v.obj = jdict; + } break; + + case Variant::POOL_INT_ARRAY: { + + PoolVector array = *p_arg; + jintArray arr = env->NewIntArray(array.size()); + PoolVector::Read r = array.read(); + env->SetIntArrayRegion(arr, 0, array.size(), r.ptr()); + v.val.l = arr; + v.obj = arr; + + } break; + case Variant::POOL_BYTE_ARRAY: { + PoolVector array = *p_arg; + jbyteArray arr = env->NewByteArray(array.size()); + PoolVector::Read r = array.read(); + env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast(r.ptr())); + v.val.l = arr; + v.obj = arr; + + } break; + case Variant::POOL_REAL_ARRAY: { + + PoolVector array = *p_arg; + jfloatArray arr = env->NewFloatArray(array.size()); + PoolVector::Read r = array.read(); + env->SetFloatArrayRegion(arr, 0, array.size(), r.ptr()); + v.val.l = arr; + v.obj = arr; + + } break; + default: { + + v.val.i = 0; + } break; + } + return v; +} + +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); + + if (array) { + jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z"); + jboolean isarr = env->CallBooleanMethod(cls, isArray); + (*array) = isarr ? true : false; + } + String name = jstring_to_string(clsName, env); + env->DeleteLocalRef(clsName); + + return name; +} + +Variant _jobject_to_variant(JNIEnv *env, jobject obj) { + + if (obj == NULL) { + return Variant(); + } + + jclass c = env->GetObjectClass(obj); + bool array; + 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); + PoolVector sarr; + + for (int i = 0; i < stringCount; i++) { + jstring string = (jstring)env->GetObjectArrayElement(arr, i); + sarr.push_back(jstring_to_string(string, env)); + env->DeleteLocalRef(string); + } + + return sarr; + }; + + 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); + return ret; + }; + + if (name == "[I") { + + jintArray arr = (jintArray)obj; + int fCount = env->GetArrayLength(arr); + PoolVector sarr; + sarr.resize(fCount); + + PoolVector::Write w = sarr.write(); + env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); + w = PoolVector::Write(); + return sarr; + }; + + if (name == "[B") { + + jbyteArray arr = (jbyteArray)obj; + int fCount = env->GetArrayLength(arr); + PoolVector sarr; + sarr.resize(fCount); + + PoolVector::Write w = sarr.write(); + env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast(w.ptr())); + w = PoolVector::Write(); + return sarr; + }; + + 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); + return ret; + }; + + if (name == "[D") { + + jdoubleArray arr = (jdoubleArray)obj; + int fCount = env->GetArrayLength(arr); + PoolRealArray sarr; + sarr.resize(fCount); + + PoolRealArray::Write w = sarr.write(); + + for (int i = 0; i < fCount; i++) { + + double n; + env->GetDoubleArrayRegion(arr, i, 1, &n); + w.ptr()[i] = n; + }; + return sarr; + }; + + if (name == "[F") { + + jfloatArray arr = (jfloatArray)obj; + int fCount = env->GetArrayLength(arr); + PoolRealArray sarr; + sarr.resize(fCount); + + PoolRealArray::Write w = sarr.write(); + + for (int i = 0; i < fCount; i++) { + + float n; + env->GetFloatArrayRegion(arr, i, 1, &n); + w.ptr()[i] = n; + }; + return sarr; + }; + + if (name == "[Ljava.lang.Object;") { + + jobjectArray arr = (jobjectArray)obj; + int objCount = env->GetArrayLength(arr); + Array varr; + + for (int i = 0; i < objCount; i++) { + jobject jobj = env->GetObjectArrayElement(arr, i); + Variant v = _jobject_to_variant(env, jobj); + varr.push_back(v); + env->DeleteLocalRef(jobj); + } + + return varr; + }; + + 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;"); + jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys); + + PoolStringArray keys = _jobject_to_variant(env, arr); + env->DeleteLocalRef(arr); + + jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;"); + arr = (jobjectArray)env->CallObjectMethod(obj, get_values); + + Array vals = _jobject_to_variant(env, arr); + env->DeleteLocalRef(arr); + + for (int i = 0; i < keys.size(); i++) { + + ret[keys[i]] = vals[i]; + }; + + return ret; + }; + + env->DeleteLocalRef(c); + + return Variant(); +} + +class JNISingleton : public Object { + + GDCLASS(JNISingleton, Object); + + struct MethodData { + + jmethodID method; + Variant::Type ret_type; + Vector argtypes; + }; + + jobject instance; + Map method_map; + +public: + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + + ERR_FAIL_COND_V(!instance, Variant()); + + r_error.error = Variant::CallError::CALL_OK; + + Map::Element *E = method_map.find(p_method); + if (!E) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + int ac = E->get().argtypes.size(); + if (ac < p_argcount) { + + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = ac; + return Variant(); + } + + if (ac > p_argcount) { + + r_error.error = Variant::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 = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = i; + r_error.expected = E->get().argtypes[i]; + } + } + + jvalue *v = NULL; + + 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 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::REAL: { + + 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::POOL_STRING_ARRAY: { + + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); + + ret = _jobject_to_variant(env, arr); + + env->DeleteLocalRef(arr); + } break; + case Variant::POOL_INT_ARRAY: { + + jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + PoolVector sarr; + sarr.resize(fCount); + + PoolVector::Write w = sarr.write(); + env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); + w = PoolVector::Write(); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + case Variant::POOL_REAL_ARRAY: { + + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + PoolVector sarr; + sarr.resize(fCount); + + PoolVector::Write w = sarr.write(); + env->GetFloatArrayRegion(arr, 0, fCount, w.ptr()); + w = PoolVector::Write(); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + + 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(NULL); + ERR_FAIL_V(Variant()); + } break; + } + + while (to_erase.size()) { + env->DeleteLocalRef(to_erase.front()->get()); + to_erase.pop_front(); + } + + env->PopLocalFrame(NULL); + + 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 &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 = NULL; + } +}; + +struct TST { + + int a; + TST() { + + a = 5; + } +}; + +TST tst; + +static bool initialized = false; +static int step = 0; + +static Size2 new_size; +static Vector3 accelerometer; +static Vector3 gravity; +static Vector3 magnetometer; +static Vector3 gyroscope; +static HashMap jni_singletons; + +// virtual Error native_video_play(String p_path); +// virtual bool native_video_is_playing(); +// virtual void native_video_pause(); +// virtual void native_video_stop(); + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height) { + if (godot_io_java) { + godot_io_java->set_vk_height(p_height); + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) { + + initialized = true; + + JavaVM *jvm; + env->GetJavaVM(&jvm); + + // create our wrapper classes + godot_java = new GodotJavaWrapper(env, activity); // our activity is our godot instance is our activity.. + godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env)); + + ThreadAndroid::make_default(jvm); +#ifdef USE_JAVA_FILE_ACCESS + FileAccessJAndroid::setup(godot_io_java->get_instance()); +#else + + jobject amgr = env->NewGlobalRef(p_asset_manager); + + FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr); +#endif + + DirAccessJAndroid::setup(godot_io_java->get_instance()); + AudioDriverAndroid::setup(godot_io_java->get_instance()); + + os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion); + + char wd[500]; + getcwd(wd, 500); + + godot_java->on_video_init(env); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env) { + // lets cleanup + if (godot_io_java) { + delete godot_io_java; + } + if (godot_java) { + delete godot_java; + } + if (os_android) { + delete os_android; + } +} + +static void _initialize_java_modules() { + + if (!ProjectSettings::get_singleton()->has_setting("android/modules")) { + return; + } + + String modules = ProjectSettings::get_singleton()->get("android/modules"); + modules = modules.strip_edges(); + if (modules == String()) { + return; + } + Vector mods = modules.split(",", false); + + if (mods.size()) { + jobject cls = godot_java->get_class_loader(); + + // TODO create wrapper for class loader + + JNIEnv *env = ThreadAndroid::get_env(); + jclass classLoader = env->FindClass("java/lang/ClassLoader"); + jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + for (int i = 0; i < mods.size(); i++) { + + String m = mods[i]; + + print_line("Loading Android module: " + m); + jstring strClassName = env->NewStringUTF(m.utf8().get_data()); + jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName); + + if (!singletonClass) { + + ERR_EXPLAIN("Couldn't find singleton for class: " + m); + ERR_CONTINUE(!singletonClass); + } + + jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lorg/godotengine/godot/Godot$SingletonBase;"); + + if (!initialize) { + + ERR_EXPLAIN("Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: " + m); + ERR_CONTINUE(!initialize); + } + jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, godot_java->get_activity()); + env->NewGlobalRef(obj); + } + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) { + ThreadAndroid::setup_thread(); + + const char **cmdline = NULL; + jstring *j_cmdline = NULL; + int cmdlen = 0; + if (p_cmdline) { + cmdlen = env->GetArrayLength(p_cmdline); + if (cmdlen) { + cmdline = (const char **)malloc((cmdlen + 1) * sizeof(const char *)); + cmdline[cmdlen] = NULL; + 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); + + cmdline[i] = rawString; + j_cmdline[i] = string; + } + } + } + + Error err = Main::setup("apk", cmdlen, (char **)cmdline, false); + if (cmdline) { + if (j_cmdline) { + for (int i = 0; i < cmdlen; ++i) { + env->ReleaseStringUTFChars(j_cmdline[i], cmdline[i]); + } + free(j_cmdline); + } + free(cmdline); + } + + if (err != OK) { + return; //should exit instead and print the error + } + + java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity())); + Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", java_class_wrapper)); + _initialize_java_modules(); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height) { + + if (os_android) + os_android->set_display_size(Size2(width, height)); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { + + if (os_android) { + if (step == 0) { + // During startup + os_android->set_context_is_16_bits(!p_32_bits); + } else { + // GL 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 + } + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { + if (step == 0) + return; + + os_android->main_loop_request_go_back(); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { + if (step == -1) + 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()); + ++step; + return; + } + + if (step == 1) { + if (!Main::start()) { + return; //should exit instead and print the error + } + + os_android->main_loop_begin(); + ++step; + } + + os_android->process_accelerometer(accelerometer); + os_android->process_gravity(gravity); + os_android->process_magnetometer(magnetometer); + os_android->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, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { + + if (step == 0) + return; + + Vector points; + for (int i = 0; i < count; i++) { + + jint p[3]; + env->GetIntArrayRegion(positions, i * 3, 3, p); + OS_Android::TouchPos tp; + tp.pos = Point2(p[1], p[2]); + tp.id = p[0]; + points.push_back(tp); + } + + os_android->process_touch(ev, pointer, points); + + /* + if (os_android) + os_android->process_touch(ev,pointer,points); + */ +} + +/* + * Android Key codes. + */ +enum { + AKEYCODE_UNKNOWN = 0, + AKEYCODE_SOFT_LEFT = 1, + AKEYCODE_SOFT_RIGHT = 2, + AKEYCODE_HOME = 3, + AKEYCODE_BACK = 4, + AKEYCODE_CALL = 5, + AKEYCODE_ENDCALL = 6, + AKEYCODE_0 = 7, + AKEYCODE_1 = 8, + AKEYCODE_2 = 9, + AKEYCODE_3 = 10, + AKEYCODE_4 = 11, + AKEYCODE_5 = 12, + AKEYCODE_6 = 13, + AKEYCODE_7 = 14, + AKEYCODE_8 = 15, + AKEYCODE_9 = 16, + AKEYCODE_STAR = 17, + AKEYCODE_POUND = 18, + AKEYCODE_DPAD_UP = 19, + AKEYCODE_DPAD_DOWN = 20, + AKEYCODE_DPAD_LEFT = 21, + AKEYCODE_DPAD_RIGHT = 22, + AKEYCODE_DPAD_CENTER = 23, + AKEYCODE_VOLUME_UP = 24, + AKEYCODE_VOLUME_DOWN = 25, + AKEYCODE_POWER = 26, + AKEYCODE_CAMERA = 27, + AKEYCODE_CLEAR = 28, + AKEYCODE_A = 29, + AKEYCODE_B = 30, + AKEYCODE_C = 31, + AKEYCODE_D = 32, + AKEYCODE_E = 33, + AKEYCODE_F = 34, + AKEYCODE_G = 35, + AKEYCODE_H = 36, + AKEYCODE_I = 37, + AKEYCODE_J = 38, + AKEYCODE_K = 39, + AKEYCODE_L = 40, + AKEYCODE_M = 41, + AKEYCODE_N = 42, + AKEYCODE_O = 43, + AKEYCODE_P = 44, + AKEYCODE_Q = 45, + AKEYCODE_R = 46, + AKEYCODE_S = 47, + AKEYCODE_T = 48, + AKEYCODE_U = 49, + AKEYCODE_V = 50, + AKEYCODE_W = 51, + AKEYCODE_X = 52, + AKEYCODE_Y = 53, + AKEYCODE_Z = 54, + AKEYCODE_COMMA = 55, + AKEYCODE_PERIOD = 56, + AKEYCODE_ALT_LEFT = 57, + AKEYCODE_ALT_RIGHT = 58, + AKEYCODE_SHIFT_LEFT = 59, + AKEYCODE_SHIFT_RIGHT = 60, + AKEYCODE_TAB = 61, + AKEYCODE_SPACE = 62, + AKEYCODE_SYM = 63, + AKEYCODE_EXPLORER = 64, + AKEYCODE_ENVELOPE = 65, + AKEYCODE_ENTER = 66, + AKEYCODE_DEL = 67, + AKEYCODE_GRAVE = 68, + AKEYCODE_MINUS = 69, + AKEYCODE_EQUALS = 70, + AKEYCODE_LEFT_BRACKET = 71, + AKEYCODE_RIGHT_BRACKET = 72, + AKEYCODE_BACKSLASH = 73, + AKEYCODE_SEMICOLON = 74, + AKEYCODE_APOSTROPHE = 75, + AKEYCODE_SLASH = 76, + AKEYCODE_AT = 77, + AKEYCODE_NUM = 78, + AKEYCODE_HEADSETHOOK = 79, + AKEYCODE_FOCUS = 80, // *Camera* focus + AKEYCODE_PLUS = 81, + AKEYCODE_MENU = 82, + AKEYCODE_NOTIFICATION = 83, + AKEYCODE_SEARCH = 84, + AKEYCODE_MEDIA_PLAY_PAUSE = 85, + AKEYCODE_MEDIA_STOP = 86, + AKEYCODE_MEDIA_NEXT = 87, + AKEYCODE_MEDIA_PREVIOUS = 88, + AKEYCODE_MEDIA_REWIND = 89, + AKEYCODE_MEDIA_FAST_FORWARD = 90, + AKEYCODE_MUTE = 91, + AKEYCODE_PAGE_UP = 92, + AKEYCODE_PAGE_DOWN = 93, + AKEYCODE_PICTSYMBOLS = 94, + AKEYCODE_SWITCH_CHARSET = 95, + AKEYCODE_BUTTON_A = 96, + AKEYCODE_BUTTON_B = 97, + AKEYCODE_BUTTON_C = 98, + AKEYCODE_BUTTON_X = 99, + AKEYCODE_BUTTON_Y = 100, + AKEYCODE_BUTTON_Z = 101, + AKEYCODE_BUTTON_L1 = 102, + AKEYCODE_BUTTON_R1 = 103, + AKEYCODE_BUTTON_L2 = 104, + AKEYCODE_BUTTON_R2 = 105, + AKEYCODE_BUTTON_THUMBL = 106, + AKEYCODE_BUTTON_THUMBR = 107, + AKEYCODE_BUTTON_START = 108, + AKEYCODE_BUTTON_SELECT = 109, + AKEYCODE_BUTTON_MODE = 110, + + // NOTE: If you add a new keycode here you must also add it to several other files. + // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. +}; + +struct _WinTranslatePair { + + unsigned int keysym; + unsigned int keycode; +}; + +static _WinTranslatePair _ak_to_keycode[] = { + { KEY_TAB, AKEYCODE_TAB }, + { KEY_ENTER, AKEYCODE_ENTER }, + { KEY_SHIFT, AKEYCODE_SHIFT_LEFT }, + { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT }, + { KEY_ALT, AKEYCODE_ALT_LEFT }, + { KEY_ALT, AKEYCODE_ALT_RIGHT }, + { KEY_MENU, AKEYCODE_MENU }, + { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE }, + { KEY_ESCAPE, AKEYCODE_BACK }, + { KEY_SPACE, AKEYCODE_SPACE }, + { KEY_PAGEUP, AKEYCODE_PAGE_UP }, + { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN }, + { KEY_HOME, AKEYCODE_HOME }, //(0x24) + { KEY_LEFT, AKEYCODE_DPAD_LEFT }, + { KEY_UP, AKEYCODE_DPAD_UP }, + { KEY_RIGHT, AKEYCODE_DPAD_RIGHT }, + { KEY_DOWN, AKEYCODE_DPAD_DOWN }, + { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER }, + { KEY_BACKSPACE, AKEYCODE_DEL }, + { KEY_0, AKEYCODE_0 }, ////0 key + { KEY_1, AKEYCODE_1 }, ////1 key + { KEY_2, AKEYCODE_2 }, ////2 key + { KEY_3, AKEYCODE_3 }, ////3 key + { KEY_4, AKEYCODE_4 }, ////4 key + { KEY_5, AKEYCODE_5 }, ////5 key + { KEY_6, AKEYCODE_6 }, ////6 key + { KEY_7, AKEYCODE_7 }, ////7 key + { KEY_8, AKEYCODE_8 }, ////8 key + { KEY_9, AKEYCODE_9 }, ////9 key + { KEY_A, AKEYCODE_A }, ////A key + { KEY_B, AKEYCODE_B }, ////B key + { KEY_C, AKEYCODE_C }, ////C key + { KEY_D, AKEYCODE_D }, ////D key + { KEY_E, AKEYCODE_E }, ////E key + { KEY_F, AKEYCODE_F }, ////F key + { KEY_G, AKEYCODE_G }, ////G key + { KEY_H, AKEYCODE_H }, ////H key + { KEY_I, AKEYCODE_I }, ////I key + { KEY_J, AKEYCODE_J }, ////J key + { KEY_K, AKEYCODE_K }, ////K key + { KEY_L, AKEYCODE_L }, ////L key + { KEY_M, AKEYCODE_M }, ////M key + { KEY_N, AKEYCODE_N }, ////N key + { KEY_O, AKEYCODE_O }, ////O key + { KEY_P, AKEYCODE_P }, ////P key + { KEY_Q, AKEYCODE_Q }, ////Q key + { KEY_R, AKEYCODE_R }, ////R key + { KEY_S, AKEYCODE_S }, ////S key + { KEY_T, AKEYCODE_T }, ////T key + { KEY_U, AKEYCODE_U }, ////U key + { KEY_V, AKEYCODE_V }, ////V key + { KEY_W, AKEYCODE_W }, ////W key + { KEY_X, AKEYCODE_X }, ////X key + { KEY_Y, AKEYCODE_Y }, ////Y key + { KEY_Z, AKEYCODE_Z }, ////Z key + { KEY_HOMEPAGE, AKEYCODE_EXPLORER }, + { KEY_LAUNCH0, AKEYCODE_BUTTON_A }, + { KEY_LAUNCH1, AKEYCODE_BUTTON_B }, + { KEY_LAUNCH2, AKEYCODE_BUTTON_C }, + { KEY_LAUNCH3, AKEYCODE_BUTTON_X }, + { KEY_LAUNCH4, AKEYCODE_BUTTON_Y }, + { KEY_LAUNCH5, AKEYCODE_BUTTON_Z }, + { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 }, + { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 }, + { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 }, + { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 }, + { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL }, + { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR }, + { KEY_LAUNCHC, AKEYCODE_BUTTON_START }, + { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT }, + { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE }, + { KEY_VOLUMEMUTE, AKEYCODE_MUTE }, + { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN }, + { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP }, + { KEY_BACK, AKEYCODE_MEDIA_REWIND }, + { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD }, + { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT }, + { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS }, + { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP }, + { KEY_PLUS, AKEYCODE_PLUS }, + { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key + { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key + { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key + { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key + { KEY_BACKSLASH, AKEYCODE_BACKSLASH }, + { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, + { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, + { KEY_UNKNOWN, 0 } +}; +/* +TODO: map these android key: + AKEYCODE_SOFT_LEFT = 1, + AKEYCODE_SOFT_RIGHT = 2, + AKEYCODE_CALL = 5, + AKEYCODE_ENDCALL = 6, + AKEYCODE_STAR = 17, + AKEYCODE_POUND = 18, + AKEYCODE_POWER = 26, + AKEYCODE_CAMERA = 27, + AKEYCODE_CLEAR = 28, + AKEYCODE_SYM = 63, + AKEYCODE_ENVELOPE = 65, + AKEYCODE_GRAVE = 68, + AKEYCODE_SEMICOLON = 74, + AKEYCODE_APOSTROPHE = 75, + AKEYCODE_AT = 77, + AKEYCODE_NUM = 78, + AKEYCODE_HEADSETHOOK = 79, + AKEYCODE_FOCUS = 80, // *Camera* focus + AKEYCODE_NOTIFICATION = 83, + AKEYCODE_SEARCH = 84, + AKEYCODE_PICTSYMBOLS = 94, + AKEYCODE_SWITCH_CHARSET = 95, +*/ + +static 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; + } + } + + return KEY_UNKNOWN; +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) { + if (step == 0) + return; + + OS_Android::JoypadEvent jevent; + jevent.device = p_device; + jevent.type = OS_Android::JOY_EVENT_BUTTON; + jevent.index = p_button; + jevent.pressed = p_pressed; + + os_android->process_joy_event(jevent); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { + if (step == 0) + return; + + OS_Android::JoypadEvent jevent; + jevent.device = p_device; + jevent.type = OS_Android::JOY_EVENT_AXIS; + jevent.index = p_axis; + jevent.value = p_value; + + os_android->process_joy_event(jevent); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { + if (step == 0) + return; + + OS_Android::JoypadEvent jevent; + jevent.device = p_device; + jevent.type = OS_Android::JOY_EVENT_HAT; + int hat = 0; + if (p_hat_x != 0) { + if (p_hat_x < 0) + hat |= InputDefault::HAT_MASK_LEFT; + else + hat |= InputDefault::HAT_MASK_RIGHT; + } + if (p_hat_y != 0) { + if (p_hat_y < 0) + hat |= InputDefault::HAT_MASK_UP; + else + hat |= InputDefault::HAT_MASK_DOWN; + } + jevent.hat = hat; + + os_android->process_joy_event(jevent); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, 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); + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { + if (step == 0) + return; + + Ref ievent; + ievent.instance(); + int val = p_unicode_char; + int scancode = android_get_keysym(p_scancode); + ievent->set_scancode(scancode); + ievent->set_unicode(val); + ievent->set_pressed(p_pressed); + + if (val == '\n') { + ievent->set_scancode(KEY_ENTER); + } else if (val == 61448) { + ievent->set_scancode(KEY_BACKSPACE); + ievent->set_unicode(KEY_BACKSPACE); + } else if (val == 61453) { + ievent->set_scancode(KEY_ENTER); + ievent->set_unicode(KEY_ENTER); + } else if (p_scancode == 4) { + + os_android->main_loop_request_go_back(); + } + + os_android->process_event(ievent); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { + accelerometer = Vector3(x, y, z); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { + gravity = Vector3(x, y, z); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { + magnetometer = Vector3(x, y, z); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { + gyroscope = Vector3(x, y, z); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { + + if (step == 0) + return; + + os_android->main_loop_focusin(); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { + + if (step == 0) + return; + + os_android->main_loop_focusout(); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { + + ThreadAndroid::setup_thread(); + AudioDriverAndroid::thread_func(env); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object) { + + String singname = jstring_to_string(name, env); + JNISingleton *s = memnew(JNISingleton); + s->set_instance(env->NewGlobalRef(p_object)); + jni_singletons[singname] = s; + + Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); + ProjectSettings::get_singleton()->set(singname, s); +} + +static Variant::Type get_jni_type(const String &p_type) { + + static struct { + const char *name; + Variant::Type type; + } _type_to_vtype[] = { + { "void", Variant::NIL }, + { "boolean", Variant::BOOL }, + { "int", Variant::INT }, + { "float", Variant::REAL }, + { "double", Variant::REAL }, + { "java.lang.String", Variant::STRING }, + { "[I", Variant::POOL_INT_ARRAY }, + { "[B", Variant::POOL_BYTE_ARRAY }, + { "[F", Variant::POOL_REAL_ARRAY }, + { "[Ljava.lang.String;", Variant::POOL_STRING_ARRAY }, + { "org.godotengine.godot.Dictionary", Variant::DICTIONARY }, + { NULL, Variant::NIL } + }; + + int idx = 0; + + while (_type_to_vtype[idx].name) { + + if (p_type == _type_to_vtype[idx].name) + return _type_to_vtype[idx].type; + + idx++; + } + + return Variant::NIL; +} + +static const char *get_jni_sig(const String &p_type) { + + static struct { + const char *name; + const char *sig; + } _type_to_vtype[] = { + { "void", "V" }, + { "boolean", "Z" }, + { "int", "I" }, + { "float", "F" }, + { "double", "D" }, + { "java.lang.String", "Ljava/lang/String;" }, + { "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" }, + { "[I", "[I" }, + { "[B", "[B" }, + { "[F", "[F" }, + { "[Ljava.lang.String;", "[Ljava/lang/String;" }, + { NULL, "V" } + }; + + int idx = 0; + + while (_type_to_vtype[idx].name) { + + if (p_type == _type_to_vtype[idx].name) + return _type_to_vtype[idx].sig; + + idx++; + } + + return "Ljava/lang/Object;"; +} + +JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, 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_method(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)); + + JNISingleton *s = jni_singletons.get(singname); + + String mname = jstring_to_string(name, env); + String retval = jstring_to_string(ret, env); + Vector types; + String cs = "("; + + 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)); + cs += get_jni_sig(rawString); + } + + cs += ")"; + cs += get_jni_sig(retval); + 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_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) { + + Object *obj = ObjectDB::get_instance(ID); + ERR_FAIL_COND(!obj); + + int res = env->PushLocalFrame(16); + ERR_FAIL_COND(res != 0); + + String str_method = jstring_to_string(method, env); + + int count = env->GetArrayLength(params); + 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) + v = _jobject_to_variant(env, obj); + memnew_placement(&vlist[i], Variant); + vlist[i] = v; + vptr[i] = &vlist[i]; + env->DeleteLocalRef(obj); + }; + + Variant::CallError err; + obj->call(str_method, (const Variant **)vptr, count, err); + // something + + env->PopLocalFrame(NULL); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) { + + Object *obj = ObjectDB::get_instance(ID); + ERR_FAIL_COND(!obj); + + int res = env->PushLocalFrame(16); + ERR_FAIL_COND(res != 0); + + String str_method = jstring_to_string(method, env); + + int count = env->GetArrayLength(params); + 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); + env->DeleteLocalRef(obj); + }; + + obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4]); + // something + env->PopLocalFrame(NULL); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result) { + String permission = jstring_to_string(p_permission, env); + if (permission == "android.permission.RECORD_AUDIO" && p_result) { + AudioDriver::get_singleton()->capture_start(); + } +} diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h new file mode 100644 index 0000000000..3a03294b08 --- /dev/null +++ b/platform/android/java_godot_lib_jni.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* java_godot_lib_jni.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 JAVA_GODOT_LIB_JNI_H +#define JAVA_GODOT_LIB_JNI_H + +#include +#include + +// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code. +// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes thats why we have the long names) +extern "C" { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); +JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result); +} + +#endif /* !JAVA_GODOT_LIB_JNI_H */ diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp new file mode 100644 index 0000000000..101a1d76c6 --- /dev/null +++ b/platform/android/java_godot_wrapper.cpp @@ -0,0 +1,185 @@ +/*************************************************************************/ +/* java_godot_wrapper.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "java_godot_wrapper.h" + +// JNIEnv is only valid within the thread it belongs to, in a multi threading environment +// we can't cache it. +// For Godot we call most access methods from our thread and we thus get a valid JNIEnv +// from ThreadAndroid. For one or two we expect to pass the environment + +// TODO we could probably create a base class for this... + +GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { + godot_instance = p_env->NewGlobalRef(p_godot_instance); + + // get info about our Godot class so we can get pointers and stuff... + cls = p_env->FindClass("org/godotengine/godot/Godot"); + if (cls) { + cls = (jclass)p_env->NewGlobalRef(cls); + } else { + // this is a pretty serious fail.. bail... pointers will stay 0 + return; + } + + // get some method pointers... + _on_video_init = p_env->GetMethodID(cls, "onVideoInit", "()V"); + _restart = p_env->GetMethodID(cls, "restart", "()V"); + _finish = p_env->GetMethodID(cls, "forceQuit", "()V"); + _set_keep_screen_on = p_env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); + _alert = p_env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); + _get_GLES_version_code = p_env->GetMethodID(cls, "getGLESVersionCode", "()I"); + _get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); + _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); + _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); +} + +GodotJavaWrapper::~GodotJavaWrapper() { + // nothing to do here for now +} + +jobject GodotJavaWrapper::get_activity() { + // our godot instance is our activity + return godot_instance; +} + +jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) { + if (cls) { + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + jfieldID fid = p_env->GetStaticFieldID(cls, p_name, p_class); + return p_env->GetStaticObjectField(cls, fid); + } else { + return NULL; + } +} + +jobject GodotJavaWrapper::get_class_loader() { + if (cls) { + JNIEnv *env = ThreadAndroid::get_env(); + jmethodID getClassLoader = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;"); + return env->CallObjectMethod(godot_instance, getClassLoader); + } else { + return NULL; + } +} + +void GodotJavaWrapper::gfx_init(bool gl2) { + // beats me what this once did, there was no code, + // but we're getting false if our GLES3 driver is initialised + // and true for our GLES2 driver + // Maybe we're supposed to communicate this back or store it? +} + +void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { + if (_on_video_init) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _on_video_init); +} + +void GodotJavaWrapper::restart(JNIEnv *p_env) { + if (_restart) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _restart); +} + +void GodotJavaWrapper::force_quit(JNIEnv *p_env) { + if (_finish) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _finish); +} + +void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { + if (_set_keep_screen_on) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled); + } +} + +void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { + if (_alert) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data()); + jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data()); + env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle); + } +} + +int GodotJavaWrapper::get_gles_version_code() { + JNIEnv *env = ThreadAndroid::get_env(); + if (_get_GLES_version_code) { + return env->CallIntMethod(godot_instance, _get_GLES_version_code); + } + + return 0; +} + +bool GodotJavaWrapper::has_get_clipboard() { + return _get_clipboard != 0; +} + +String GodotJavaWrapper::get_clipboard() { + if (_get_clipboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +bool GodotJavaWrapper::has_set_clipboard() { + return _set_clipboard != 0; +} + +void GodotJavaWrapper::set_clipboard(const String &p_text) { + if (_set_clipboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); + env->CallVoidMethod(godot_instance, _set_clipboard, jStr); + } +} + +bool GodotJavaWrapper::request_permission(const String &p_name) { + if (_request_permission) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); + return env->CallBooleanMethod(godot_instance, _request_permission, jStrName); + } else { + return false; + } +} diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h new file mode 100644 index 0000000000..438aee019b --- /dev/null +++ b/platform/android/java_godot_wrapper.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* java_godot_wrapper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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. */ +/*************************************************************************/ + +// note, swapped java and godot around in the file name so all the java +// wrappers are together + +#ifndef JAVA_GODOT_WRAPPER_H +#define JAVA_GODOT_WRAPPER_H + +#include +#include + +#include "string_android.h" + +// Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++ +class GodotJavaWrapper { +private: + jobject godot_instance; + jclass cls; + + jmethodID _on_video_init = 0; + jmethodID _restart = 0; + jmethodID _finish = 0; + jmethodID _set_keep_screen_on = 0; + jmethodID _alert = 0; + jmethodID _get_GLES_version_code = 0; + jmethodID _get_clipboard = 0; + jmethodID _set_clipboard = 0; + jmethodID _request_permission = 0; + +public: + GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); + ~GodotJavaWrapper(); + + jobject get_activity(); + jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = NULL); + + jobject get_class_loader(); + + void gfx_init(bool gl2); + void on_video_init(JNIEnv *p_env = NULL); + void restart(JNIEnv *p_env = NULL); + void force_quit(JNIEnv *p_env = NULL); + void set_keep_screen_on(bool p_enabled); + void alert(const String &p_message, const String &p_title); + int get_gles_version_code(); + bool has_get_clipboard(); + String get_clipboard(); + bool has_set_clipboard(); + void set_clipboard(const String &p_text); + bool request_permission(const String &p_name); +}; + +#endif /* !JAVA_GODOT_WRAPPER_H */ diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 837713f9c9..d65cf2f31e 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -46,6 +46,9 @@ #include +#include "java_godot_io_wrapper.h" +#include "java_godot_wrapper.h" + class AndroidLogger : public Logger { public: virtual void logv(const char *p_format, va_list p_list, bool p_err) { @@ -118,15 +121,14 @@ int OS_Android::get_current_video_driver() const { Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - bool use_gl3 = get_gl_version_code_func() >= 0x00030000; + bool use_gl3 = godot_java->get_gles_version_code() >= 0x00030000; use_gl3 = use_gl3 && (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3"); bool gl_initialization_error = false; while (true) { if (use_gl3) { if (RasterizerGLES3::is_viable() == OK) { - if (gfx_init_func) - gfx_init_func(gfx_init_ud, false); + godot_java->gfx_init(false); RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); break; @@ -142,8 +144,7 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int } } else { if (RasterizerGLES2::is_viable() == OK) { - if (gfx_init_func) - gfx_init_func(gfx_init_ud, true); + godot_java->gfx_init(true); RasterizerGLES2::register_config(); RasterizerGLES2::make_current(); break; @@ -195,17 +196,23 @@ void OS_Android::finalize() { memdelete(input); } +GodotJavaWrapper *OS_Android::get_godot_java() { + return godot_java; +} + +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()); - if (alert_func) - alert_func(p_alert, p_title); + godot_java->alert(p_alert, p_title); } bool OS_Android::request_permission(const String &p_name) { - if (request_permission_func) - return request_permission_func(p_name); - return false; + + return godot_java->request_permission(p_name); } Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { @@ -262,9 +269,7 @@ void OS_Android::get_fullscreen_mode_list(List *p_list, int p_screen) void OS_Android::set_keep_screen_on(bool p_enabled) { OS::set_keep_screen_on(p_enabled); - if (set_keep_screen_on_func) { - set_keep_screen_on_func(p_enabled); - } + godot_java->set_keep_screen_on(p_enabled); } Size2 OS_Android::get_window_size() const { @@ -505,18 +510,16 @@ bool OS_Android::has_virtual_keyboard() const { } int OS_Android::get_virtual_keyboard_height() const { - if (get_virtual_keyboard_height_func) { - return get_virtual_keyboard_height_func(); - } + return godot_io_java->get_vk_height(); - ERR_PRINT("Cannot obtain virtual keyboard height."); - return 0; + // 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) { - if (show_virtual_keyboard_func) { - show_virtual_keyboard_func(p_existing_text); + if (godot_io_java->has_vk()) { + godot_io_java->show_vk(p_existing_text); } else { ERR_PRINT("Virtual keyboard not available"); @@ -525,9 +528,9 @@ void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect void OS_Android::hide_virtual_keyboard() { - if (hide_virtual_keyboard_func) { + if (godot_io_java->has_vk()) { - hide_virtual_keyboard_func(); + godot_io_java->hide_vk(); } else { ERR_PRINT("Virtual keyboard not available"); @@ -556,9 +559,7 @@ void OS_Android::set_display_size(Size2 p_size) { Error OS_Android::shell_open(String p_uri) { - if (open_uri_func) - return open_uri_func(p_uri) ? ERR_CANT_OPEN : OK; - return ERR_UNAVAILABLE; + return godot_io_java->open_uri(p_uri); } String OS_Android::get_resource_dir() const { @@ -568,23 +569,29 @@ String OS_Android::get_resource_dir() const { String OS_Android::get_locale() const { - if (get_locale_func) - return get_locale_func(); + String locale = godot_io_java->get_locale(); + if (locale != "") { + return locale; + } + return OS_Unix::get_locale(); } void OS_Android::set_clipboard(const String &p_text) { - if (set_clipboard_func) { - set_clipboard_func(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 { - if (get_clipboard_func) { - return get_clipboard_func(); + + // 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(); @@ -592,17 +599,16 @@ String OS_Android::get_clipboard() const { String OS_Android::get_model_name() const { - if (get_model_func) - return get_model_func(); + String model = godot_io_java->get_model(); + if (model != "") + return model; + return OS_Unix::get_model_name(); } int OS_Android::get_screen_dpi(int p_screen) const { - if (get_screen_dpi_func) { - return get_screen_dpi_func(); - } - return 160; + return godot_io_java->get_screen_dpi(); } String OS_Android::get_user_data_dir() const { @@ -610,8 +616,8 @@ String OS_Android::get_user_data_dir() const { if (data_dir_cache != String()) return data_dir_cache; - if (get_user_data_dir_func) { - String data_dir = get_user_data_dir_func(); + String data_dir = godot_io_java->get_user_data_dir(); + if (data_dir != "") { //store current dir char real_current_dir_name[2048]; @@ -638,45 +644,43 @@ String OS_Android::get_user_data_dir() const { void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { - if (set_screen_orientation_func) - set_screen_orientation_func(p_orientation); + godot_io_java->set_screen_orientation(p_orientation); } String OS_Android::get_unique_id() const { - if (get_unique_id_func) - return get_unique_id_func(); + String unique_id = godot_io_java->get_unique_id(); + if (unique_id != "") + return unique_id; + 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 - if (video_play_func) - video_play_func(p_path); + + godot_io_java->play_video(p_path); return OK; } bool OS_Android::native_video_is_playing() const { - if (video_is_playing_func) - return video_is_playing_func(); - return false; + + return godot_io_java->is_video_playing(); } void OS_Android::native_video_pause() { - if (video_pause_func) - video_pause_func(); + + godot_io_java->pause_video(); } String OS_Android::get_system_dir(SystemDir p_dir) const { - if (get_system_dir_func) - return get_system_dir_func(p_dir); - return String("."); + return godot_io_java->get_system_dir(p_dir); } void OS_Android::native_video_stop() { - if (video_stop_func) - video_stop_func(); + + godot_io_java->stop_video(); } void OS_Android::set_context_is_16_bits(bool p_is_16) { @@ -719,7 +723,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { return false; } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard_func, GetClipboardFunc p_get_clipboard_func, RequestPermissionFunc p_request_permission, bool p_use_apk_expansion) { +OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -727,38 +731,13 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI default_videomode.fullscreen = true; default_videomode.resizable = false; - gfx_init_func = p_gfx_init_func; - gfx_init_ud = p_gfx_init_ud; main_loop = NULL; gl_extensions = NULL; //rasterizer = NULL; use_gl2 = false; - open_uri_func = p_open_uri_func; - get_user_data_dir_func = p_get_user_data_dir_func; - get_locale_func = p_get_locale_func; - get_model_func = p_get_model_func; - get_screen_dpi_func = p_get_screen_dpi_func; - get_unique_id_func = p_get_unique_id; - get_system_dir_func = p_get_sdir_func; - get_gl_version_code_func = p_get_gl_version_func; - - video_play_func = p_video_play_func; - video_is_playing_func = p_video_is_playing_func; - video_pause_func = p_video_pause_func; - video_stop_func = p_video_stop_func; - - show_virtual_keyboard_func = p_show_vk; - hide_virtual_keyboard_func = p_hide_vk; - get_virtual_keyboard_height_func = p_vk_height_func; - - set_clipboard_func = p_set_clipboard_func; - get_clipboard_func = p_get_clipboard_func; - - set_screen_orientation_func = p_screen_orient; - set_keep_screen_on_func = p_set_keep_screen_on_func; - alert_func = p_alert_func; - request_permission_func = p_request_permission; + godot_java = p_godot_java; + godot_io_java = p_godot_io_java; Vector loggers; loggers.push_back(memnew(AndroidLogger)); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 44c5a206d4..3a5404124a 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -41,29 +41,8 @@ #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -typedef void (*GFXInitFunc)(void *ud, bool gl2); -typedef int (*OpenURIFunc)(const String &); -typedef String (*GetUserDataDirFunc)(); -typedef String (*GetLocaleFunc)(); -typedef void (*SetClipboardFunc)(const String &); -typedef String (*GetClipboardFunc)(); -typedef String (*GetModelFunc)(); -typedef int (*GetScreenDPIFunc)(); -typedef String (*GetUniqueIDFunc)(); -typedef void (*ShowVirtualKeyboardFunc)(const String &); -typedef void (*HideVirtualKeyboardFunc)(); -typedef void (*SetScreenOrientationFunc)(int); -typedef String (*GetSystemDirFunc)(int); -typedef int (*GetGLVersionCodeFunc)(); - -typedef void (*VideoPlayFunc)(const String &); -typedef bool (*VideoIsPlayingFunc)(); -typedef void (*VideoPauseFunc)(); -typedef void (*VideoStopFunc)(); -typedef void (*SetKeepScreenOnFunc)(bool p_enabled); -typedef void (*AlertFunc)(const String &, const String &); -typedef int (*VirtualKeyboardHeightFunc)(); -typedef bool (*RequestPermissionFunc)(const String &); +class GodotJavaWrapper; +class GodotIOJavaWrapper; class OS_Android : public OS_Unix { public: @@ -91,9 +70,6 @@ public: private: Vector touch; - GFXInitFunc gfx_init_func; - void *gfx_init_ud; - bool use_gl2; bool use_apk_expansion; @@ -112,30 +88,11 @@ private: VideoMode default_videomode; MainLoop *main_loop; - OpenURIFunc open_uri_func; - GetUserDataDirFunc get_user_data_dir_func; - GetLocaleFunc get_locale_func; - SetClipboardFunc set_clipboard_func; - GetClipboardFunc get_clipboard_func; - GetModelFunc get_model_func; - GetScreenDPIFunc get_screen_dpi_func; - ShowVirtualKeyboardFunc show_virtual_keyboard_func; - HideVirtualKeyboardFunc hide_virtual_keyboard_func; - VirtualKeyboardHeightFunc get_virtual_keyboard_height_func; - SetScreenOrientationFunc set_screen_orientation_func; - GetUniqueIDFunc get_unique_id_func; - GetSystemDirFunc get_system_dir_func; - GetGLVersionCodeFunc get_gl_version_code_func; - - VideoPlayFunc video_play_func; - VideoIsPlayingFunc video_is_playing_func; - VideoPauseFunc video_pause_func; - VideoStopFunc video_stop_func; - SetKeepScreenOnFunc set_keep_screen_on_func; - AlertFunc alert_func; - RequestPermissionFunc request_permission_func; - - //PowerAndroid *power_manager; + GodotJavaWrapper *godot_java; + GodotIOJavaWrapper *godot_io_java; + + //PowerAndroid *power_manager_func; + int video_driver_index; public: @@ -159,6 +116,8 @@ public: typedef int64_t ProcessID; static OS *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); @@ -241,7 +200,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard, GetClipboardFunc p_get_clipboard, RequestPermissionFunc p_request_permission, bool p_use_apk_expansion); + OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); ~OS_Android(); }; -- cgit v1.2.3