porting_android.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  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 "settings.h"
  27. #include <sstream>
  28. #include <exception>
  29. #include <cstdlib>
  30. #ifdef GPROF
  31. #include "prof.h"
  32. #endif
  33. extern int main(int argc, char *argv[]);
  34. extern "C" JNIEXPORT void JNICALL
  35. Java_net_minetest_minetest_GameActivity_saveSettings(JNIEnv* env, jobject /* this */) {
  36. if (!g_settings_path.empty())
  37. g_settings->updateConfigFile(g_settings_path.c_str());
  38. }
  39. namespace porting {
  40. // used here:
  41. void cleanupAndroid();
  42. std::string getLanguageAndroid();
  43. bool setSystemPaths(); // used in porting.cpp
  44. }
  45. void android_main(android_app *app)
  46. {
  47. porting::app_global = app;
  48. Thread::setName("Main");
  49. char *argv[] = {strdup(PROJECT_NAME), strdup("--verbose"), nullptr};
  50. int retval = main(ARRLEN(argv) - 1, argv);
  51. free(argv[0]);
  52. free(argv[1]);
  53. porting::cleanupAndroid();
  54. infostream << "Shutting down." << std::endl;
  55. exit(retval);
  56. }
  57. namespace porting {
  58. android_app *app_global = nullptr;
  59. JNIEnv *jnienv = nullptr;
  60. jclass nativeActivity;
  61. jclass findClass(const std::string &classname)
  62. {
  63. if (jnienv == nullptr)
  64. return nullptr;
  65. jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity");
  66. jmethodID getClassLoader = jnienv->GetMethodID(
  67. nativeactivity, "getClassLoader", "()Ljava/lang/ClassLoader;");
  68. jobject cls = jnienv->CallObjectMethod(
  69. app_global->activity->clazz, getClassLoader);
  70. jclass classLoader = jnienv->FindClass("java/lang/ClassLoader");
  71. jmethodID findClass = jnienv->GetMethodID(classLoader, "loadClass",
  72. "(Ljava/lang/String;)Ljava/lang/Class;");
  73. jstring strClassName = jnienv->NewStringUTF(classname.c_str());
  74. return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName);
  75. }
  76. void osSpecificInit()
  77. {
  78. JavaVM *jvm = app_global->activity->vm;
  79. JavaVMAttachArgs lJavaVMAttachArgs;
  80. lJavaVMAttachArgs.version = JNI_VERSION_1_6;
  81. lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread";
  82. lJavaVMAttachArgs.group = nullptr;
  83. if (jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) {
  84. errorstream << "Failed to attach native thread to jvm" << std::endl;
  85. exit(-1);
  86. }
  87. nativeActivity = findClass("net/minetest/minetest/GameActivity");
  88. if (nativeActivity == nullptr)
  89. errorstream <<
  90. "porting::initAndroid unable to find Java native activity class" <<
  91. std::endl;
  92. // Set default language
  93. auto lang = getLanguageAndroid();
  94. unsetenv("LANGUAGE");
  95. setenv("LANG", lang.c_str(), 1);
  96. #ifdef GPROF
  97. // in the start-up code
  98. warningstream << "Initializing GPROF profiler" << std::endl;
  99. monstartup("libMinetest.so");
  100. #endif
  101. }
  102. void cleanupAndroid()
  103. {
  104. #ifdef GPROF
  105. warningstream << "Shutting down GPROF profiler" << std::endl;
  106. setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1);
  107. moncleanup();
  108. #endif
  109. JavaVM *jvm = app_global->activity->vm;
  110. jvm->DetachCurrentThread();
  111. }
  112. static std::string readJavaString(jstring j_str)
  113. {
  114. // Get string as a UTF-8 C string
  115. const char *c_str = jnienv->GetStringUTFChars(j_str, nullptr);
  116. // Save it
  117. std::string str(c_str);
  118. // And free the C string
  119. jnienv->ReleaseStringUTFChars(j_str, c_str);
  120. return str;
  121. }
  122. bool setSystemPaths()
  123. {
  124. // Set user and share paths
  125. {
  126. jmethodID getUserDataPath = jnienv->GetMethodID(nativeActivity,
  127. "getUserDataPath", "()Ljava/lang/String;");
  128. FATAL_ERROR_IF(getUserDataPath==nullptr,
  129. "porting::initializePathsAndroid unable to find Java getUserDataPath method");
  130. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getUserDataPath);
  131. std::string str = readJavaString((jstring) result);
  132. path_user = str;
  133. path_share = str;
  134. }
  135. // Set cache path
  136. {
  137. jmethodID getCachePath = jnienv->GetMethodID(nativeActivity,
  138. "getCachePath", "()Ljava/lang/String;");
  139. FATAL_ERROR_IF(getCachePath==nullptr,
  140. "porting::initializePathsAndroid unable to find Java getCachePath method");
  141. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getCachePath);
  142. path_cache = readJavaString((jstring) result);
  143. }
  144. return true;
  145. }
  146. void showTextInputDialog(const std::string &hint, const std::string &current, int editType)
  147. {
  148. jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showTextInputDialog",
  149. "(Ljava/lang/String;Ljava/lang/String;I)V");
  150. FATAL_ERROR_IF(showdialog == nullptr,
  151. "porting::showTextInputDialog unable to find Java showTextInputDialog method");
  152. jstring jhint = jnienv->NewStringUTF(hint.c_str());
  153. jstring jcurrent = jnienv->NewStringUTF(current.c_str());
  154. jint jeditType = editType;
  155. jnienv->CallVoidMethod(app_global->activity->clazz, showdialog,
  156. jhint, jcurrent, jeditType);
  157. }
  158. void showComboBoxDialog(const std::string optionList[], s32 listSize, s32 selectedIdx)
  159. {
  160. jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showSelectionInputDialog",
  161. "([Ljava/lang/String;I)V");
  162. FATAL_ERROR_IF(showdialog == nullptr,
  163. "porting::showComboBoxDialog unable to find Java showSelectionInputDialog method");
  164. jclass jStringClass = jnienv->FindClass("java/lang/String");
  165. jobjectArray jOptionList = jnienv->NewObjectArray(listSize, jStringClass, NULL);
  166. jint jselectedIdx = selectedIdx;
  167. for (s32 i = 0; i < listSize; i ++) {
  168. jnienv->SetObjectArrayElement(jOptionList, i,
  169. jnienv->NewStringUTF(optionList[i].c_str()));
  170. }
  171. jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, jOptionList,
  172. jselectedIdx);
  173. }
  174. void openURIAndroid(const char *url)
  175. {
  176. jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURI",
  177. "(Ljava/lang/String;)V");
  178. FATAL_ERROR_IF(url_open == nullptr,
  179. "porting::openURIAndroid unable to find Java openURI method");
  180. jstring jurl = jnienv->NewStringUTF(url);
  181. jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl);
  182. }
  183. void shareFileAndroid(const std::string &path)
  184. {
  185. jmethodID url_open = jnienv->GetMethodID(nativeActivity, "shareFile",
  186. "(Ljava/lang/String;)V");
  187. FATAL_ERROR_IF(url_open == nullptr,
  188. "porting::shareFileAndroid unable to find Java shareFile method");
  189. jstring jurl = jnienv->NewStringUTF(path.c_str());
  190. jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl);
  191. }
  192. AndroidDialogType getLastInputDialogType()
  193. {
  194. jmethodID lastdialogtype = jnienv->GetMethodID(nativeActivity,
  195. "getLastDialogType", "()I");
  196. FATAL_ERROR_IF(lastdialogtype == nullptr,
  197. "porting::getLastInputDialogType unable to find Java getLastDialogType method");
  198. int dialogType = jnienv->CallIntMethod(app_global->activity->clazz, lastdialogtype);
  199. return static_cast<AndroidDialogType>(dialogType);
  200. }
  201. AndroidDialogState getInputDialogState()
  202. {
  203. jmethodID inputdialogstate = jnienv->GetMethodID(nativeActivity,
  204. "getInputDialogState", "()I");
  205. FATAL_ERROR_IF(inputdialogstate == nullptr,
  206. "porting::getInputDialogState unable to find Java getInputDialogState method");
  207. int dialogState = jnienv->CallIntMethod(app_global->activity->clazz, inputdialogstate);
  208. return static_cast<AndroidDialogState>(dialogState);
  209. }
  210. std::string getInputDialogMessage()
  211. {
  212. jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity,
  213. "getDialogMessage", "()Ljava/lang/String;");
  214. FATAL_ERROR_IF(dialogvalue == nullptr,
  215. "porting::getInputDialogMessage unable to find Java getDialogMessage method");
  216. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
  217. dialogvalue);
  218. return readJavaString((jstring) result);
  219. }
  220. int getInputDialogSelection()
  221. {
  222. jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity, "getDialogSelection", "()I");
  223. FATAL_ERROR_IF(dialogvalue == nullptr,
  224. "porting::getInputDialogSelection unable to find Java getDialogSelection method");
  225. return jnienv->CallIntMethod(app_global->activity->clazz, dialogvalue);
  226. }
  227. #ifndef SERVER
  228. float getDisplayDensity()
  229. {
  230. static bool firstrun = true;
  231. static float value = 0;
  232. if (firstrun) {
  233. jmethodID getDensity = jnienv->GetMethodID(nativeActivity,
  234. "getDensity", "()F");
  235. FATAL_ERROR_IF(getDensity == nullptr,
  236. "porting::getDisplayDensity unable to find Java getDensity method");
  237. value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity);
  238. firstrun = false;
  239. }
  240. return value;
  241. }
  242. v2u32 getDisplaySize()
  243. {
  244. static bool firstrun = true;
  245. static v2u32 retval;
  246. if (firstrun) {
  247. jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity,
  248. "getDisplayWidth", "()I");
  249. FATAL_ERROR_IF(getDisplayWidth == nullptr,
  250. "porting::getDisplayWidth unable to find Java getDisplayWidth method");
  251. retval.X = jnienv->CallIntMethod(app_global->activity->clazz,
  252. getDisplayWidth);
  253. jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity,
  254. "getDisplayHeight", "()I");
  255. FATAL_ERROR_IF(getDisplayHeight == nullptr,
  256. "porting::getDisplayHeight unable to find Java getDisplayHeight method");
  257. retval.Y = jnienv->CallIntMethod(app_global->activity->clazz,
  258. getDisplayHeight);
  259. firstrun = false;
  260. }
  261. return retval;
  262. }
  263. std::string getLanguageAndroid()
  264. {
  265. jmethodID getLanguage = jnienv->GetMethodID(nativeActivity,
  266. "getLanguage", "()Ljava/lang/String;");
  267. FATAL_ERROR_IF(getLanguage == nullptr,
  268. "porting::getLanguageAndroid unable to find Java getLanguage method");
  269. jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
  270. getLanguage);
  271. return readJavaString((jstring) result);
  272. }
  273. #endif // ndef SERVER
  274. }