porting_android.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. Minetest
  3. Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #ifndef __ANDROID__
  17. #error This file may only be compiled for android!
  18. #endif
  19. #include "util/numeric.h"
  20. #include "porting.h"
  21. #include "porting_android.h"
  22. #include "threading/thread.h"
  23. #include "config.h"
  24. #include "filesys.h"
  25. #include "log.h"
  26. #include <sstream>
  27. #include <exception>
  28. #include <cstdlib>
  29. #ifdef GPROF
  30. #include "prof.h"
  31. #endif
  32. extern int main(int argc, char *argv[]);
  33. void android_main(android_app *app)
  34. {
  35. int retval = 0;
  36. porting::app_global = app;
  37. Thread::setName("Main");
  38. try {
  39. app_dummy();
  40. char *argv[] = {strdup(PROJECT_NAME), NULL};
  41. main(ARRLEN(argv) - 1, argv);
  42. free(argv[0]);
  43. } catch (std::exception &e) {
  44. errorstream << "Uncaught exception in main thread: " << e.what() << std::endl;
  45. retval = -1;
  46. } catch (...) {
  47. errorstream << "Uncaught exception in main thread!" << std::endl;
  48. retval = -1;
  49. }
  50. porting::cleanupAndroid();
  51. infostream << "Shutting down." << std::endl;
  52. exit(retval);
  53. }
  54. /* handler for finished message box input */
  55. /* Intentionally NOT in namespace porting */
  56. /* TODO this doesn't work as expected, no idea why but there's a workaround */
  57. /* for it right now */
  58. extern "C" {
  59. JNIEXPORT void JNICALL Java_net_minetest_MtNativeActivity_putMessageBoxResult(
  60. JNIEnv * env, jclass thiz, jstring text)
  61. {
  62. errorstream << "Java_net_minetest_MtNativeActivity_putMessageBoxResult got: "
  63. << std::string((const char*)env->GetStringChars(text,0))
  64. << std::endl;
  65. }
  66. }
  67. namespace porting {
  68. std::string path_storage = DIR_DELIM "sdcard" DIR_DELIM;
  69. android_app* app_global;
  70. JNIEnv* jnienv;
  71. jclass nativeActivity;
  72. jclass findClass(std::string classname)
  73. {
  74. if (jnienv == 0) {
  75. return 0;
  76. }
  77. jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity");
  78. jmethodID getClassLoader =
  79. jnienv->GetMethodID(nativeactivity,"getClassLoader",
  80. "()Ljava/lang/ClassLoader;");
  81. jobject cls =
  82. jnienv->CallObjectMethod(app_global->activity->clazz, getClassLoader);
  83. jclass classLoader = jnienv->FindClass("java/lang/ClassLoader");
  84. jmethodID findClass =
  85. jnienv->GetMethodID(classLoader, "loadClass",
  86. "(Ljava/lang/String;)Ljava/lang/Class;");
  87. jstring strClassName =
  88. jnienv->NewStringUTF(classname.c_str());
  89. return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName);
  90. }
  91. void copyAssets()
  92. {
  93. jmethodID assetcopy = jnienv->GetMethodID(nativeActivity,"copyAssets","()V");
  94. if (assetcopy == 0) {
  95. assert("porting::copyAssets unable to find copy assets method" == 0);
  96. }
  97. jnienv->CallVoidMethod(app_global->activity->clazz, assetcopy);
  98. }
  99. void initAndroid()
  100. {
  101. porting::jnienv = NULL;
  102. JavaVM *jvm = app_global->activity->vm;
  103. JavaVMAttachArgs lJavaVMAttachArgs;
  104. lJavaVMAttachArgs.version = JNI_VERSION_1_6;
  105. lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread";
  106. lJavaVMAttachArgs.group = NULL;
  107. #ifdef NDEBUG
  108. // This is a ugly hack as arm v7a non debuggable builds crash without this
  109. // printf ... if someone finds out why please fix it!
  110. infostream << "Attaching native thread. " << std::endl;
  111. #endif
  112. if ( jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) {
  113. errorstream << "Failed to attach native thread to jvm" << std::endl;
  114. exit(-1);
  115. }
  116. nativeActivity = findClass("net/minetest/minetest/MtNativeActivity");
  117. if (nativeActivity == 0) {
  118. errorstream <<
  119. "porting::initAndroid unable to find java native activity class" <<
  120. std::endl;
  121. }
  122. #ifdef GPROF
  123. /* in the start-up code */
  124. __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C,
  125. "Initializing GPROF profiler");
  126. monstartup("libminetest.so");
  127. #endif
  128. }
  129. void cleanupAndroid()
  130. {
  131. #ifdef GPROF
  132. errorstream << "Shutting down GPROF profiler" << std::endl;
  133. setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1);
  134. moncleanup();
  135. #endif
  136. JavaVM *jvm = app_global->activity->vm;
  137. jvm->DetachCurrentThread();
  138. }
  139. static std::string javaStringToUTF8(jstring js)
  140. {
  141. std::string str;
  142. // Get string as a UTF-8 c-string
  143. const char *c_str = jnienv->GetStringUTFChars(js, NULL);
  144. // Save it
  145. str = c_str;
  146. // And free the c-string
  147. jnienv->ReleaseStringUTFChars(js, c_str);
  148. return str;
  149. }
  150. // Calls static method if obj is NULL
  151. static std::string getAndroidPath(jclass cls, jobject obj, jclass cls_File,
  152. jmethodID mt_getAbsPath, const char *getter)
  153. {
  154. // Get getter method
  155. jmethodID mt_getter;
  156. if (obj)
  157. mt_getter = jnienv->GetMethodID(cls, getter,
  158. "()Ljava/io/File;");
  159. else
  160. mt_getter = jnienv->GetStaticMethodID(cls, getter,
  161. "()Ljava/io/File;");
  162. // Call getter
  163. jobject ob_file;
  164. if (obj)
  165. ob_file = jnienv->CallObjectMethod(obj, mt_getter);
  166. else
  167. ob_file = jnienv->CallStaticObjectMethod(cls, mt_getter);
  168. // Call getAbsolutePath
  169. jstring js_path = (jstring) jnienv->CallObjectMethod(ob_file,
  170. mt_getAbsPath);
  171. return javaStringToUTF8(js_path);
  172. }
  173. void initializePathsAndroid()
  174. {
  175. // Get Environment class
  176. jclass cls_Env = jnienv->FindClass("android/os/Environment");
  177. // Get File class
  178. jclass cls_File = jnienv->FindClass("java/io/File");
  179. // Get getAbsolutePath method
  180. jmethodID mt_getAbsPath = jnienv->GetMethodID(cls_File,
  181. "getAbsolutePath", "()Ljava/lang/String;");
  182. path_cache = getAndroidPath(nativeActivity, app_global->activity->clazz,
  183. cls_File, mt_getAbsPath, "getCacheDir");
  184. path_storage = getAndroidPath(cls_Env, NULL, cls_File, mt_getAbsPath,
  185. "getExternalStorageDirectory");
  186. path_user = path_storage + DIR_DELIM + PROJECT_NAME_C;
  187. path_share = path_storage + DIR_DELIM + PROJECT_NAME_C;
  188. migrateCachePath();
  189. }
  190. void showInputDialog(const std::string& acceptButton, const std::string& hint,
  191. const std::string& current, int editType)
  192. {
  193. jmethodID showdialog = jnienv->GetMethodID(nativeActivity,"showDialog",
  194. "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
  195. if (showdialog == 0) {
  196. assert("porting::showInputDialog unable to find java show dialog method" == 0);
  197. }
  198. jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str());
  199. jstring jhint = jnienv->NewStringUTF(hint.c_str());
  200. jstring jcurrent = jnienv->NewStringUTF(current.c_str());
  201. jint jeditType = editType;
  202. jnienv->CallVoidMethod(app_global->activity->clazz, showdialog,
  203. jacceptButton, jhint, jcurrent, jeditType);
  204. }
  205. int getInputDialogState()
  206. {
  207. jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
  208. "getDialogState", "()I");
  209. if (dialogstate == 0) {
  210. assert("porting::getInputDialogState unable to find java dialog state method" == 0);
  211. }
  212. return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate);
  213. }
  214. std::string getInputDialogValue()
  215. {
  216. jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity,
  217. "getDialogValue", "()Ljava/lang/String;");
  218. if (dialogvalue == 0) {
  219. assert("porting::getInputDialogValue unable to find java dialog value method" == 0);
  220. }
  221. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
  222. dialogvalue);
  223. const char* javachars = jnienv->GetStringUTFChars((jstring) result,0);
  224. std::string text(javachars);
  225. jnienv->ReleaseStringUTFChars((jstring) result, javachars);
  226. return text;
  227. }
  228. #ifndef SERVER
  229. float getDisplayDensity()
  230. {
  231. static bool firstrun = true;
  232. static float value = 0;
  233. if (firstrun) {
  234. jmethodID getDensity = jnienv->GetMethodID(nativeActivity, "getDensity",
  235. "()F");
  236. if (getDensity == 0) {
  237. assert("porting::getDisplayDensity unable to find java getDensity method" == 0);
  238. }
  239. value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity);
  240. firstrun = false;
  241. }
  242. return value;
  243. }
  244. v2u32 getDisplaySize()
  245. {
  246. static bool firstrun = true;
  247. static v2u32 retval;
  248. if (firstrun) {
  249. jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity,
  250. "getDisplayWidth", "()I");
  251. if (getDisplayWidth == 0) {
  252. assert("porting::getDisplayWidth unable to find java getDisplayWidth method" == 0);
  253. }
  254. retval.X = jnienv->CallIntMethod(app_global->activity->clazz,
  255. getDisplayWidth);
  256. jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity,
  257. "getDisplayHeight", "()I");
  258. if (getDisplayHeight == 0) {
  259. assert("porting::getDisplayHeight unable to find java getDisplayHeight method" == 0);
  260. }
  261. retval.Y = jnienv->CallIntMethod(app_global->activity->clazz,
  262. getDisplayHeight);
  263. firstrun = false;
  264. }
  265. return retval;
  266. }
  267. #endif // ndef SERVER
  268. }