From b38283a6b7e495b3f6748c2c5e47352bd1861f2b Mon Sep 17 00:00:00 2001 From: fhuya Date: Wed, 4 Mar 2020 09:21:59 -0800 Subject: [PATCH] Miscellaneous cleanup for the Android codebase: - update gradle plugins versions - add formatting rules for AndroidManifest and gradle build files - cleanup java_godot_lib_jni Note: logic was mostly moved around and no new logic/functionality was added. --- .editorconfig | 4 + .gitignore | 2 +- platform/android/SCsub | 2 + platform/android/android_keys_utils.cpp | 43 + platform/android/android_keys_utils.h | 280 +++++ platform/android/java/app/build.gradle | 12 +- platform/android/java/app/config.gradle | 30 +- platform/android/java/build.gradle | 28 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- platform/android/java_godot_lib_jni.cpp | 1030 ++--------------- platform/android/java_godot_lib_jni.h | 60 +- platform/android/jni_utils.cpp | 434 +++++++ platform/android/jni_utils.h | 242 ++++ 13 files changed, 1156 insertions(+), 1013 deletions(-) create mode 100644 platform/android/android_keys_utils.cpp create mode 100644 platform/android/android_keys_utils.h create mode 100644 platform/android/jni_utils.cpp create mode 100644 platform/android/jni_utils.h diff --git a/.editorconfig b/.editorconfig index 56cc2e9c2dd..f335026e1e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,10 @@ insert_final_newline = true [*.{cpp,hpp,c,h,mm}] trim_trailing_whitespace = true +[{*.gradle,AndroidManifest.xml}] +indent_style = space +indent_size = 4 + [{*.{py,cs},SConstruct,SCsub}] indent_style = space indent_size = 4 diff --git a/.gitignore b/.gitignore index 25a36c8e5f0..9b68510bb3e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ local.properties .gradletasknamecache project.properties platform/android/java/libs/* -platform/android/java/assets +platform/android/java/app/libs/* # General c++ generated files *.lib diff --git a/platform/android/SCsub b/platform/android/SCsub index fd2a774c71e..9db41a315fe 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -18,6 +18,8 @@ android_files = [ 'java_class_wrapper.cpp', 'java_godot_wrapper.cpp', 'java_godot_io_wrapper.cpp', + 'jni_utils.cpp', + 'android_keys_utils.cpp' ] env_android = env.Clone() diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp new file mode 100644 index 00000000000..88874ba2c7c --- /dev/null +++ b/platform/android/android_keys_utils.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* android_keys_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "android_keys_utils.h" + +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; +} diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h new file mode 100644 index 00000000000..f076688ac82 --- /dev/null +++ b/platform/android/android_keys_utils.h @@ -0,0 +1,280 @@ +/*************************************************************************/ +/* android_keys_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANDROID_KEYS_UTILS_H +#define ANDROID_KEYS_UTILS_H + +#include + +/* + * 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, +*/ + +unsigned int android_get_keysym(unsigned int p_code); + +#endif // ANDROID_KEYS_UTILS_H diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 2e4f2ffab0e..990fb35c46c 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -80,33 +80,33 @@ android { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = [ - 'src' + 'src' //DIR_SRC_BEGIN //DIR_SRC_END ] res.srcDirs = [ - 'res' + 'res' //DIR_RES_BEGIN //DIR_RES_END ] aidl.srcDirs = [ - 'aidl' + 'aidl' //DIR_AIDL_BEGIN //DIR_AIDL_END ] assets.srcDirs = [ - 'assets' + 'assets' //DIR_ASSETS_BEGIN //DIR_ASSETS_END ] } debug.jniLibs.srcDirs = [ - 'libs/debug' + 'libs/debug' //DIR_JNI_DEBUG_BEGIN //DIR_JNI_DEBUG_END ] release.jniLibs.srcDirs = [ - 'libs/release' + 'libs/release' //DIR_JNI_RELEASE_BEGIN //DIR_JNI_RELEASE_END ] diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 5550d3099db..7a983a1cc8e 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,24 +1,24 @@ ext.versions = [ - androidGradlePlugin : '3.4.2', - compileSdk : 28, - minSdk : 18, - targetSdk : 28, - buildTools : '28.0.3', - supportCoreUtils : '28.0.0' + androidGradlePlugin: '3.6.0', + compileSdk : 29, + minSdk : 18, + targetSdk : 29, + buildTools : '29.0.1', + supportCoreUtils : '28.0.0' ] ext.libraries = [ - androidGradlePlugin : "com.android.tools.build:gradle:$versions.androidGradlePlugin", - supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils" + androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin", + supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils" ] ext.getExportPackageName = { -> - // Retrieve the app id from the project property set by the Godot build command. - String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : "" - // Check if the app id is valid, otherwise use the default. - if (appId == null || appId.isEmpty()) { - appId = "com.godot.game" - } - return appId + // Retrieve the app id from the project property set by the Godot build command. + String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : "" + // Check if the app id is valid, otherwise use the default. + if (appId == null || appId.isEmpty()) { + appId = "com.godot.game" + } + return appId } diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 20520178882..01cb78653d9 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -24,7 +24,7 @@ ext { sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : "" supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"] - supportedTargets = ['release':"release", 'debug':"release_debug"] + supportedTargets = ['release': "release", 'debug': "release_debug"] // Used by gradle to specify which architecture to build for by default when running `./gradlew build`. // This command is usually used by Android Studio. @@ -95,7 +95,7 @@ task zipCustomBuild(type: Zip) { doFirst { logger.lifecycle("Generating Godot custom build template") } - from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties','gradlew', 'gradlew.bat', 'gradle/**'])) + from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**'])) include '**/*' archiveName 'android_source.zip' destinationDir(file(binDir)) @@ -133,20 +133,20 @@ task generateGodotTemplates(type: GradleBuild) { * Clean the generated artifacts. */ task cleanGodotTemplates(type: Delete) { - // Delete the generated native libs - delete("lib/libs") + // Delete the generated native libs + delete("lib/libs") - // Delete the library generated AAR files - delete("lib/build/outputs/aar") + // Delete the library generated AAR files + delete("lib/build/outputs/aar") - // Delete the app libs directory contents - delete("app/libs") + // Delete the app libs directory contents + delete("app/libs") - // Delete the generated binary apks - delete("app/build/outputs/apk") + // Delete the generated binary apks + delete("app/build/outputs/apk") - // Delete the Godot templates in the Godot bin directory - delete("$binDir/android_debug.apk") - delete("$binDir/android_release.apk") - delete("$binDir/android_source.zip") + // Delete the Godot templates in the Godot bin directory + delete("$binDir/android_debug.apk") + delete("$binDir/android_release.apk") + delete("$binDir/android_source.zip") } diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index bf50265715e..f56b0f6a5e5 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 9768ee9ebea..b95acc78b95 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -33,14 +33,15 @@ #include "java_godot_wrapper.h" #include "android/asset_manager_jni.h" +#include "android_keys_utils.h" #include "api/java_class_wrapper.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 "jni_utils.h" #include "main/input_default.h" #include "main/main.h" #include "net_socket_android.h" @@ -54,547 +55,6 @@ 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::FLOAT: { - - 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::PACKED_STRING_ARRAY: { - - Vector 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::PACKED_INT32_ARRAY: { - - Vector array = *p_arg; - jintArray arr = env->NewIntArray(array.size()); - const int *r = array.ptr(); - env->SetIntArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; - - } break; - case Variant::PACKED_BYTE_ARRAY: { - Vector array = *p_arg; - jbyteArray arr = env->NewByteArray(array.size()); - const uint8_t *r = array.ptr(); - env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast(r)); - v.val.l = arr; - v.obj = arr; - - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - - Vector array = *p_arg; - jfloatArray arr = env->NewFloatArray(array.size()); - const float *r = array.ptr(); - env->SetFloatArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; - - } break; -#ifndef _MSC_VER -#warning This is missing 64 bits arrays, I have no idea how to do it in JNI -#endif - - 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); - Vector 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); - Vector sarr; - sarr.resize(fCount); - - int *w = sarr.ptrw(); - env->GetIntArrayRegion(arr, 0, fCount, w); - return sarr; - }; - - if (name == "[B") { - - jbyteArray arr = (jbyteArray)obj; - int fCount = env->GetArrayLength(arr); - Vector sarr; - sarr.resize(fCount); - - uint8_t *w = sarr.ptrw(); - env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast(w)); - 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); - PackedFloat32Array sarr; - sarr.resize(fCount); - - real_t *w = sarr.ptrw(); - - for (int i = 0; i < fCount; i++) { - - double n; - env->GetDoubleArrayRegion(arr, i, 1, &n); - w[i] = n; - }; - return sarr; - }; - - if (name == "[F") { - - jfloatArray arr = (jfloatArray)obj; - int fCount = env->GetArrayLength(arr); - PackedFloat32Array sarr; - sarr.resize(fCount); - - real_t *w = sarr.ptrw(); - - for (int i = 0; i < fCount; i++) { - - float n; - env->GetFloatArrayRegion(arr, i, 1, &n); - w[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); - - PackedStringArray 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, Callable::CallError &r_error) { - - ERR_FAIL_COND_V(!instance, Variant()); - - r_error.error = Callable::CallError::CALL_OK; - - Map::Element *E = method_map.find(p_method); - if (!E) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - - int ac = E->get().argtypes.size(); - if (ac < p_argcount) { - - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - if (ac > p_argcount) { - - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - for (int i = 0; i < p_argcount; i++) { - - if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = i; - r_error.expected = E->get().argtypes[i]; - } - } - - jvalue *v = 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::FLOAT: { - - ret = env->CallFloatMethodA(instance, E->get().method, v); - } break; - case Variant::STRING: { - - jobject o = env->CallObjectMethodA(instance, E->get().method, v); - ret = jstring_to_string((jstring)o, env); - env->DeleteLocalRef(o); - } break; - case Variant::PACKED_STRING_ARRAY: { - - jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); - - ret = _jobject_to_variant(env, arr); - - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_INT32_ARRAY: { - - jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector sarr; - sarr.resize(fCount); - - int *w = sarr.ptrw(); - env->GetIntArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - - jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector sarr; - sarr.resize(fCount); - - float *w = sarr.ptrw(); - env->GetFloatArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - -#ifndef _MSC_VER -#warning This is missing 64 bits arrays, I have no idea how to do it in JNI -#endif - case Variant::DICTIONARY: { - - jobject obj = env->CallObjectMethodA(instance, E->get().method, v); - ret = _jobject_to_variant(env, obj); - env->DeleteLocalRef(obj); - - } break; - default: { - - env->PopLocalFrame(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; @@ -605,63 +65,6 @@ 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()); - NetSocketAndroid::setup(godot_java->get_member_object("netUtils", "Lorg/godotengine/godot/utils/GodotNetUtils;", env)); - - 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, jobject obj, jobject activity) { - // 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")) { @@ -702,7 +105,61 @@ static void _initialize_java_modules() { } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) { +extern "C" { + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, 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, jclass clazz, 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()); + NetSocketAndroid::setup(godot_java->get_member_object("netUtils", "Lorg/godotengine/godot/utils/GodotNetUtils;", env)); + + 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, jclass clazz, jobject activity) { + // lets cleanup + if (godot_io_java) { + delete godot_io_java; + } + if (godot_java) { + delete godot_java; + } + if (os_android) { + delete os_android; + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) { ThreadAndroid::setup_thread(); const char **cmdline = NULL; @@ -745,13 +202,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo _initialize_java_modules(); } -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_resize(JNIEnv *env, jclass clazz, 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits) { if (os_android) { if (step == 0) { @@ -766,14 +223,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz) { if (step == 0) return; os_android->main_loop_request_go_back(); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) { if (step == -1) return; @@ -806,7 +263,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job } } -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_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) { if (step == 0) return; @@ -830,282 +287,28 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo */ } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y) { if (step == 0) return; os_android->process_hover(p_type, Point2(p_x, p_y)); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y) { if (step == 0) return; os_android->process_double_tap(Point2(p_x, p_y)); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) { if (step == 0) return; os_android->process_scroll(Point2(p_x, p_y)); } -/* - * 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) { if (step == 0) return; @@ -1118,7 +321,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) { if (step == 0) return; @@ -1131,7 +334,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) { if (step == 0) return; @@ -1156,14 +359,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name) { if (os_android) { String name = jstring_to_string(p_name, env); os_android->joy_connection_changed(p_device, p_connected, name); } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { if (step == 0) return; @@ -1192,23 +395,23 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj os_android->process_event(ievent); } -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_accelerometer(JNIEnv *env, jclass clazz, 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) { gyroscope = Vector3(x, y, z); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) { if (step == 0) return; @@ -1216,7 +419,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, os_android->main_loop_focusin(); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) { if (step == 0) return; @@ -1224,13 +427,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, os_android->main_loop_focusout(); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) { 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jclass clazz, jstring name, jobject p_object) { String singname = jstring_to_string(name, env); JNISingleton *s = memnew(JNISingleton); @@ -1241,80 +444,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env 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::FLOAT }, - { "double", Variant::FLOAT }, - { "java.lang.String", Variant::STRING }, - { "[I", Variant::PACKED_INT32_ARRAY }, - { "[B", Variant::PACKED_BYTE_ARRAY }, - { "[F", Variant::PACKED_FLOAT32_ARRAY }, - { "[Ljava.lang.String;", Variant::PACKED_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) { +JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) { String js = jstring_to_string(path, env); return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) { String singname = jstring_to_string(sname, env); @@ -1349,7 +486,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, j 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) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); ERR_FAIL_COND(!obj); @@ -1381,7 +518,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en env->PopLocalFrame(NULL); } -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_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) { Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID)); ERR_FAIL_COND(!obj); @@ -1407,7 +544,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * env->PopLocalFrame(NULL); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, 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(); @@ -1435,3 +572,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APP_PAUSED); } } +} diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index fdd84ab1efa..619940976e8 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -37,36 +37,36 @@ // 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 that's 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, jobject obj, jobject activity); -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_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_keycode, 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); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jclass clazz, jstring name, jobject p_object); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args); +JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz); } diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp new file mode 100644 index 00000000000..3fa4e808848 --- /dev/null +++ b/platform/android/jni_utils.cpp @@ -0,0 +1,434 @@ +/*************************************************************************/ +/* jni_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "jni_utils.h" + +jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) { + + jvalret v; + + switch (p_type) { + + case Variant::BOOL: { + + if (force_jobject) { + jclass bclass = env->FindClass("java/lang/Boolean"); + jmethodID ctor = env->GetMethodID(bclass, "", "(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::FLOAT: { + + 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::PACKED_STRING_ARRAY: { + + Vector 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::PACKED_INT32_ARRAY: { + + Vector array = *p_arg; + jintArray arr = env->NewIntArray(array.size()); + const int *r = array.ptr(); + env->SetIntArrayRegion(arr, 0, array.size(), r); + v.val.l = arr; + v.obj = arr; + + } break; + case Variant::PACKED_BYTE_ARRAY: { + Vector array = *p_arg; + jbyteArray arr = env->NewByteArray(array.size()); + const uint8_t *r = array.ptr(); + env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast(r)); + v.val.l = arr; + v.obj = arr; + + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + + Vector array = *p_arg; + jfloatArray arr = env->NewFloatArray(array.size()); + const float *r = array.ptr(); + env->SetFloatArrayRegion(arr, 0, array.size(), r); + v.val.l = arr; + v.obj = arr; + + } break; +#ifndef _MSC_VER +#warning This is missing 64 bits arrays, I have no idea how to do it in JNI +#endif + + 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); + Vector 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); + Vector sarr; + sarr.resize(fCount); + + int *w = sarr.ptrw(); + env->GetIntArrayRegion(arr, 0, fCount, w); + return sarr; + }; + + if (name == "[B") { + + jbyteArray arr = (jbyteArray)obj; + int fCount = env->GetArrayLength(arr); + Vector sarr; + sarr.resize(fCount); + + uint8_t *w = sarr.ptrw(); + env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast(w)); + 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); + PackedFloat32Array sarr; + sarr.resize(fCount); + + real_t *w = sarr.ptrw(); + + for (int i = 0; i < fCount; i++) { + + double n; + env->GetDoubleArrayRegion(arr, i, 1, &n); + w[i] = n; + }; + return sarr; + }; + + if (name == "[F") { + + jfloatArray arr = (jfloatArray)obj; + int fCount = env->GetArrayLength(arr); + PackedFloat32Array sarr; + sarr.resize(fCount); + + real_t *w = sarr.ptrw(); + + for (int i = 0; i < fCount; i++) { + + float n; + env->GetFloatArrayRegion(arr, i, 1, &n); + w[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); + + PackedStringArray 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(); +} + +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::FLOAT }, + { "double", Variant::FLOAT }, + { "java.lang.String", Variant::STRING }, + { "[I", Variant::PACKED_INT32_ARRAY }, + { "[B", Variant::PACKED_BYTE_ARRAY }, + { "[F", Variant::PACKED_FLOAT32_ARRAY }, + { "[Ljava.lang.String;", Variant::PACKED_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; +} + +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;"; +} diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h new file mode 100644 index 00000000000..925340a680c --- /dev/null +++ b/platform/android/jni_utils.h @@ -0,0 +1,242 @@ +/*************************************************************************/ +/* jni_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JNI_UTILS_H +#define JNI_UTILS_H + +#include "string_android.h" +#include +#include +#include + +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); + +String _get_class_name(JNIEnv *env, jclass cls, bool *array); + +Variant _jobject_to_variant(JNIEnv *env, jobject obj); + +Variant::Type get_jni_type(const String &p_type); + +const char *get_jni_sig(const String &p_type); + +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, Callable::CallError &r_error) { + + ERR_FAIL_COND_V(!instance, Variant()); + + r_error.error = Callable::CallError::CALL_OK; + + Map::Element *E = method_map.find(p_method); + if (!E) { + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + int ac = E->get().argtypes.size(); + if (ac < p_argcount) { + + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = ac; + return Variant(); + } + + if (ac > p_argcount) { + + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = ac; + return Variant(); + } + + for (int i = 0; i < p_argcount; i++) { + + if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = i; + r_error.expected = E->get().argtypes[i]; + } + } + + jvalue *v = 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::FLOAT: { + + ret = env->CallFloatMethodA(instance, E->get().method, v); + } break; + case Variant::STRING: { + + jobject o = env->CallObjectMethodA(instance, E->get().method, v); + ret = jstring_to_string((jstring)o, env); + env->DeleteLocalRef(o); + } break; + case Variant::PACKED_STRING_ARRAY: { + + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); + + ret = _jobject_to_variant(env, arr); + + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_INT32_ARRAY: { + + jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + Vector sarr; + sarr.resize(fCount); + + int *w = sarr.ptrw(); + env->GetIntArrayRegion(arr, 0, fCount, w); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); + + int fCount = env->GetArrayLength(arr); + Vector sarr; + sarr.resize(fCount); + + float *w = sarr.ptrw(); + env->GetFloatArrayRegion(arr, 0, fCount, w); + ret = sarr; + env->DeleteLocalRef(arr); + } break; + +#ifndef _MSC_VER +#warning This is missing 64 bits arrays, I have no idea how to do it in JNI +#endif + case Variant::DICTIONARY: { + + jobject obj = env->CallObjectMethodA(instance, E->get().method, v); + ret = _jobject_to_variant(env, obj); + env->DeleteLocalRef(obj); + + } break; + default: { + + env->PopLocalFrame(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; + } +}; + +#endif // JNI_UTILS_H