porting_android.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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 3.0 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 <stdlib.h>
  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. char *argv[] = {strdup(PROJECT_NAME), NULL};
  40. main(ARRLEN(argv) - 1, argv);
  41. free(argv[0]);
  42. } catch (std::exception &e) {
  43. errorstream << "Uncaught exception in main thread: " << e.what() << std::endl;
  44. retval = -1;
  45. } catch (...) {
  46. errorstream << "Uncaught exception in main thread!" << std::endl;
  47. retval = -1;
  48. }
  49. porting::cleanupAndroid();
  50. infostream << "Shutting down." << std::endl;
  51. exit(retval);
  52. }
  53. /* handler for finished message box input */
  54. /* Intentionally NOT in namespace porting */
  55. /* TODO this doesn't work as expected, no idea why but there's a workaround */
  56. /* for it right now */
  57. extern "C" {
  58. JNIEXPORT void JNICALL Java_net_blockcolor_game_GameActivity_putMessageBoxResult(
  59. JNIEnv *env, jclass j_this, jstring text)
  60. {
  61. errorstream << "Java_net_blockcolor_game_GameActivity_putMessageBoxResult got: "
  62. << std::string((const char*)env->GetStringChars(text,0))
  63. << std::endl;
  64. }
  65. }
  66. namespace porting {
  67. std::string path_storage = DIR_DELIM "sdcard" DIR_DELIM;
  68. int device_memory_max = 0;
  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 initAndroid()
  92. {
  93. porting::jnienv = NULL;
  94. JavaVM *jvm = app_global->activity->vm;
  95. JavaVMAttachArgs lJavaVMAttachArgs;
  96. lJavaVMAttachArgs.version = JNI_VERSION_1_6;
  97. lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread";
  98. lJavaVMAttachArgs.group = NULL;
  99. #ifdef NDEBUG
  100. // This is a ugly hack as arm v7a non debuggable builds crash without this
  101. // printf ... if someone finds out why please fix it!
  102. infostream << "Attaching native thread. " << std::endl;
  103. #endif
  104. if ( jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) {
  105. errorstream << "Failed to attach native thread to jvm" << std::endl;
  106. exit(-1);
  107. }
  108. nativeActivity = findClass("net/blockcolor/game/GameActivity");
  109. if (nativeActivity == 0) {
  110. errorstream <<
  111. "porting::initAndroid unable to find java native activity class" <<
  112. std::endl;
  113. }
  114. #ifdef GPROF
  115. /* in the start-up code */
  116. __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C,
  117. "Initializing GPROF profiler");
  118. monstartup("libBlockColor.so");
  119. #endif
  120. }
  121. void cleanupAndroid()
  122. {
  123. #ifdef GPROF
  124. errorstream << "Shutting down GPROF profiler" << std::endl;
  125. setenv("CPUPROFILE", (path_share + DIR_DELIM + "gmon.out").c_str(), 1);
  126. moncleanup();
  127. #endif
  128. JavaVM *jvm = app_global->activity->vm;
  129. jvm->DetachCurrentThread();
  130. }
  131. static std::string javaStringToUTF8(jstring js)
  132. {
  133. std::string str;
  134. // Get string as a UTF-8 c-string
  135. const char *c_str = jnienv->GetStringUTFChars(js, NULL);
  136. // Save it
  137. str = c_str;
  138. // And free the c-string
  139. jnienv->ReleaseStringUTFChars(js, c_str);
  140. return str;
  141. }
  142. // Calls static method if obj is NULL
  143. static std::string getAndroidPath(jclass cls, jobject obj, jclass cls_File,
  144. jmethodID mt_getAbsPath, const char *getter)
  145. {
  146. // Get getter method
  147. jmethodID mt_getter;
  148. if (obj)
  149. mt_getter = jnienv->GetMethodID(cls, getter,
  150. "()Ljava/io/File;");
  151. else
  152. mt_getter = jnienv->GetStaticMethodID(cls, getter,
  153. "()Ljava/io/File;");
  154. // Call getter
  155. jobject ob_file;
  156. if (obj)
  157. ob_file = jnienv->CallObjectMethod(obj, mt_getter);
  158. else
  159. ob_file = jnienv->CallStaticObjectMethod(cls, mt_getter);
  160. // Call getAbsolutePath
  161. jstring js_path = (jstring) jnienv->CallObjectMethod(ob_file,
  162. mt_getAbsPath);
  163. return javaStringToUTF8(js_path);
  164. }
  165. void initializePathsAndroid()
  166. {
  167. // Get Environment class
  168. jclass cls_Env = jnienv->FindClass("android/os/Environment");
  169. // Get File class
  170. jclass cls_File = jnienv->FindClass("java/io/File");
  171. // Get getAbsolutePath method
  172. jmethodID mt_getAbsPath = jnienv->GetMethodID(cls_File,
  173. "getAbsolutePath", "()Ljava/lang/String;");
  174. path_cache = getAndroidPath(nativeActivity, app_global->activity->clazz,
  175. cls_File, mt_getAbsPath, "getCacheDir");
  176. path_storage = getAndroidPath(cls_Env, NULL, cls_File, mt_getAbsPath,
  177. "getExternalStorageDirectory");
  178. path_share = path_storage + DIR_DELIM + "Android/data/net.blockcolor.game/files";
  179. path_locale = path_share + DIR_DELIM + "locale";
  180. }
  181. void showInputDialog(const std::string& acceptButton, const std::string& hint,
  182. const std::string& current, int editType)
  183. {
  184. jmethodID showdialog = jnienv->GetMethodID(nativeActivity,"showDialog",
  185. "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
  186. if (showdialog == 0) {
  187. assert("porting::showInputDialog unable to find java show dialog method" == 0);
  188. }
  189. jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str());
  190. jstring jhint = jnienv->NewStringUTF(hint.c_str());
  191. jstring jcurrent = jnienv->NewStringUTF(current.c_str());
  192. jint jeditType = editType;
  193. jnienv->CallVoidMethod(app_global->activity->clazz, showdialog,
  194. jacceptButton, jhint, jcurrent, jeditType);
  195. }
  196. int getInputDialogState()
  197. {
  198. jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
  199. "getDialogState", "()I");
  200. if (dialogstate == 0) {
  201. assert("porting::getInputDialogState unable to find java dialog state method" == 0);
  202. }
  203. return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate);
  204. }
  205. std::string getInputDialogValue()
  206. {
  207. jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity,
  208. "getDialogValue", "()Ljava/lang/String;");
  209. if (dialogvalue == 0) {
  210. assert("porting::getInputDialogValue unable to find java dialog value method" == 0);
  211. }
  212. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
  213. dialogvalue);
  214. const char* javachars = jnienv->GetStringUTFChars((jstring) result,0);
  215. std::string text(javachars);
  216. jnienv->ReleaseStringUTFChars((jstring) result, javachars);
  217. return text;
  218. }
  219. int getMemoryMax()
  220. {
  221. if (device_memory_max == 0) {
  222. jmethodID getMemory = jnienv->GetMethodID(nativeActivity,
  223. "getMemoryMax", "()I");
  224. if (getMemory == 0)
  225. assert("porting::getMemoryMax unable to find java method" == 0);
  226. device_memory_max = jnienv->CallIntMethod(
  227. app_global->activity->clazz, getMemory);
  228. }
  229. return device_memory_max;
  230. }
  231. void notifyAbortLoading()
  232. {
  233. jmethodID notifyAbort = jnienv->GetMethodID(nativeActivity,
  234. "notifyAbortLoading", "()V");
  235. if (notifyAbort == 0) {
  236. assert("porting::notifyAbortLoading unable to find java method" == 0);
  237. }
  238. jnienv->CallVoidMethod(app_global->activity->clazz, notifyAbort);
  239. }
  240. void notifyServerConnect(bool is_multiplayer)
  241. {
  242. jmethodID notifyConnect = jnienv->GetMethodID(nativeActivity,
  243. "notifyServerConnect", "(Z)V");
  244. if (notifyConnect == 0) {
  245. assert("porting::notifyServerConnect unable to find java method" == 0);
  246. }
  247. jboolean param = (jboolean)is_multiplayer;
  248. jnienv->CallVoidMethod(app_global->activity->clazz, notifyConnect, param);
  249. }
  250. void notifyExitGame()
  251. {
  252. jmethodID notifyExit = jnienv->GetMethodID(nativeActivity,
  253. "notifyExitGame", "()V");
  254. if (notifyExit == 0) {
  255. assert("porting::notifyExitGame unable to find java method" == 0);
  256. }
  257. jnienv->CallVoidMethod(app_global->activity->clazz, notifyExit);
  258. }
  259. float getDisplayDensity()
  260. {
  261. static bool firstrun = true;
  262. static float value = 0;
  263. if (firstrun) {
  264. jmethodID getDensity = jnienv->GetMethodID(nativeActivity, "getDensity",
  265. "()F");
  266. if (getDensity == 0) {
  267. assert("porting::getDisplayDensity unable to find java getDensity method" == 0);
  268. }
  269. value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity);
  270. firstrun = false;
  271. }
  272. return value;
  273. }
  274. v2u32 getDisplaySize()
  275. {
  276. static bool firstrun = true;
  277. static v2u32 retval;
  278. if (firstrun) {
  279. jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity,
  280. "getDisplayWidth", "()I");
  281. if (getDisplayWidth == 0) {
  282. assert("porting::getDisplayWidth unable to find java getDisplayWidth method" == 0);
  283. }
  284. retval.X = jnienv->CallIntMethod(app_global->activity->clazz,
  285. getDisplayWidth);
  286. jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity,
  287. "getDisplayHeight", "()I");
  288. if (getDisplayHeight == 0) {
  289. assert("porting::getDisplayHeight unable to find java getDisplayHeight method" == 0);
  290. }
  291. retval.Y = jnienv->CallIntMethod(app_global->activity->clazz,
  292. getDisplayHeight);
  293. firstrun = false;
  294. }
  295. return retval;
  296. }
  297. }