GodotIO.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. /*************************************************************************/
  2. /* GodotIO.java */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. package org.godotengine.godot;
  31. import java.util.HashMap;
  32. import java.util.Locale;
  33. import android.net.Uri;
  34. import android.content.Intent;
  35. import android.content.res.AssetManager;
  36. import java.io.InputStream;
  37. import java.io.IOException;
  38. import android.app.*;
  39. import android.content.*;
  40. import android.util.SparseArray;
  41. import android.view.*;
  42. import android.view.inputmethod.InputMethodManager;
  43. import android.os.*;
  44. import android.util.Log;
  45. import android.util.DisplayMetrics;
  46. import android.graphics.*;
  47. import android.text.method.*;
  48. import android.text.*;
  49. import android.media.*;
  50. import android.hardware.*;
  51. import android.content.*;
  52. import android.content.pm.ActivityInfo;
  53. import org.godotengine.godot.input.*;
  54. //android.os.Build
  55. // Wrapper for native library
  56. public class GodotIO {
  57. AssetManager am;
  58. Godot activity;
  59. GodotEditText edit;
  60. MediaPlayer mediaPlayer;
  61. final int SCREEN_LANDSCAPE = 0;
  62. final int SCREEN_PORTRAIT = 1;
  63. final int SCREEN_REVERSE_LANDSCAPE = 2;
  64. final int SCREEN_REVERSE_PORTRAIT = 3;
  65. final int SCREEN_SENSOR_LANDSCAPE = 4;
  66. final int SCREEN_SENSOR_PORTRAIT = 5;
  67. final int SCREEN_SENSOR = 6;
  68. /////////////////////////
  69. /// FILES
  70. /////////////////////////
  71. public int last_file_id = 1;
  72. class AssetData {
  73. public boolean eof = false;
  74. public String path;
  75. public InputStream is;
  76. public int len;
  77. public int pos;
  78. }
  79. SparseArray<AssetData> streams;
  80. public int file_open(String path, boolean write) {
  81. //System.out.printf("file_open: Attempt to Open %s\n",path);
  82. //Log.v("MyApp", "TRYING TO OPEN FILE: " + path);
  83. if (write)
  84. return -1;
  85. AssetData ad = new AssetData();
  86. try {
  87. ad.is = am.open(path);
  88. } catch (Exception e) {
  89. //System.out.printf("Exception on file_open: %s\n",path);
  90. return -1;
  91. }
  92. try {
  93. ad.len = ad.is.available();
  94. } catch (Exception e) {
  95. System.out.printf("Exception availabling on file_open: %s\n", path);
  96. return -1;
  97. }
  98. ad.path = path;
  99. ad.pos = 0;
  100. ++last_file_id;
  101. streams.put(last_file_id, ad);
  102. return last_file_id;
  103. }
  104. public int file_get_size(int id) {
  105. if (streams.get(id) == null) {
  106. System.out.printf("file_get_size: Invalid file id: %d\n", id);
  107. return -1;
  108. }
  109. return streams.get(id).len;
  110. }
  111. public void file_seek(int id, int bytes) {
  112. if (streams.get(id) == null) {
  113. System.out.printf("file_get_size: Invalid file id: %d\n", id);
  114. return;
  115. }
  116. //seek sucks
  117. AssetData ad = streams.get(id);
  118. if (bytes > ad.len)
  119. bytes = ad.len;
  120. if (bytes < 0)
  121. bytes = 0;
  122. try {
  123. if (bytes > (int)ad.pos) {
  124. int todo = bytes - (int)ad.pos;
  125. while (todo > 0) {
  126. todo -= ad.is.skip(todo);
  127. }
  128. ad.pos = bytes;
  129. } else if (bytes < (int)ad.pos) {
  130. ad.is = am.open(ad.path);
  131. ad.pos = bytes;
  132. int todo = bytes;
  133. while (todo > 0) {
  134. todo -= ad.is.skip(todo);
  135. }
  136. }
  137. ad.eof = false;
  138. } catch (IOException e) {
  139. System.out.printf("Exception on file_seek: %s\n", e);
  140. return;
  141. }
  142. }
  143. public int file_tell(int id) {
  144. if (streams.get(id) == null) {
  145. System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id);
  146. return 0;
  147. }
  148. AssetData ad = streams.get(id);
  149. return ad.pos;
  150. }
  151. public boolean file_eof(int id) {
  152. if (streams.get(id) == null) {
  153. System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id);
  154. return false;
  155. }
  156. AssetData ad = streams.get(id);
  157. return ad.eof;
  158. }
  159. public byte[] file_read(int id, int bytes) {
  160. if (streams.get(id) == null) {
  161. System.out.printf("file_read: Can't read invalid file id: %d\n", id);
  162. return new byte[0];
  163. }
  164. AssetData ad = streams.get(id);
  165. if (ad.pos + bytes > ad.len) {
  166. bytes = ad.len - ad.pos;
  167. ad.eof = true;
  168. }
  169. if (bytes == 0) {
  170. return new byte[0];
  171. }
  172. byte[] buf1 = new byte[bytes];
  173. int r = 0;
  174. try {
  175. r = ad.is.read(buf1);
  176. } catch (IOException e) {
  177. System.out.printf("Exception on file_read: %s\n", e);
  178. return new byte[bytes];
  179. }
  180. if (r == 0) {
  181. return new byte[0];
  182. }
  183. ad.pos += r;
  184. if (r < bytes) {
  185. byte[] buf2 = new byte[r];
  186. for (int i = 0; i < r; i++)
  187. buf2[i] = buf1[i];
  188. return buf2;
  189. } else {
  190. return buf1;
  191. }
  192. }
  193. public void file_close(int id) {
  194. if (streams.get(id) == null) {
  195. System.out.printf("file_close: Can't close invalid file id: %d\n", id);
  196. return;
  197. }
  198. streams.remove(id);
  199. }
  200. /////////////////////////
  201. /// DIRECTORIES
  202. /////////////////////////
  203. class AssetDir {
  204. public String[] files;
  205. public int current;
  206. public String path;
  207. }
  208. public int last_dir_id = 1;
  209. SparseArray<AssetDir> dirs;
  210. public int dir_open(String path) {
  211. AssetDir ad = new AssetDir();
  212. ad.current = 0;
  213. ad.path = path;
  214. try {
  215. ad.files = am.list(path);
  216. // no way to find path is directory or file exactly.
  217. // but if ad.files.length==0, then it's an empty directory or file.
  218. if (ad.files.length == 0) {
  219. return -1;
  220. }
  221. } catch (IOException e) {
  222. System.out.printf("Exception on dir_open: %s\n", e);
  223. return -1;
  224. }
  225. //System.out.printf("Opened dir: %s\n",path);
  226. ++last_dir_id;
  227. dirs.put(last_dir_id, ad);
  228. return last_dir_id;
  229. }
  230. public boolean dir_is_dir(int id) {
  231. if (dirs.get(id) == null) {
  232. System.out.printf("dir_next: invalid dir id: %d\n", id);
  233. return false;
  234. }
  235. AssetDir ad = dirs.get(id);
  236. //System.out.printf("go next: %d,%d\n",ad.current,ad.files.length);
  237. int idx = ad.current;
  238. if (idx > 0)
  239. idx--;
  240. if (idx >= ad.files.length)
  241. return false;
  242. String fname = ad.files[idx];
  243. try {
  244. if (ad.path.equals(""))
  245. am.open(fname);
  246. else
  247. am.open(ad.path + "/" + fname);
  248. return false;
  249. } catch (Exception e) {
  250. return true;
  251. }
  252. }
  253. public String dir_next(int id) {
  254. if (dirs.get(id) == null) {
  255. System.out.printf("dir_next: invalid dir id: %d\n", id);
  256. return "";
  257. }
  258. AssetDir ad = dirs.get(id);
  259. //System.out.printf("go next: %d,%d\n",ad.current,ad.files.length);
  260. if (ad.current >= ad.files.length) {
  261. ad.current++;
  262. return "";
  263. }
  264. String r = ad.files[ad.current];
  265. ad.current++;
  266. return r;
  267. }
  268. public void dir_close(int id) {
  269. if (dirs.get(id) == null) {
  270. System.out.printf("dir_close: invalid dir id: %d\n", id);
  271. return;
  272. }
  273. dirs.remove(id);
  274. }
  275. GodotIO(Godot p_activity) {
  276. am = p_activity.getAssets();
  277. activity = p_activity;
  278. //streams = new HashMap<Integer, AssetData>();
  279. streams = new SparseArray<AssetData>();
  280. dirs = new SparseArray<AssetDir>();
  281. }
  282. /////////////////////////
  283. // AUDIO
  284. /////////////////////////
  285. private Object buf;
  286. private Thread mAudioThread;
  287. private AudioTrack mAudioTrack;
  288. public Object audioInit(int sampleRate, int desiredFrames) {
  289. int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
  290. int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
  291. int frameSize = 4;
  292. System.out.printf("audioInit: initializing audio:\n");
  293. //Log.v("Godot", "Godot audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  294. // Let the user pick a larger buffer if they really want -- but ye
  295. // gods they probably shouldn't, the minimums are horrifyingly high
  296. // latency already
  297. desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
  298. mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
  299. channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
  300. audioStartThread();
  301. //Log.v("Godot", "Godot audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  302. buf = new short[desiredFrames * 2];
  303. return buf;
  304. }
  305. public void audioStartThread() {
  306. mAudioThread = new Thread(new Runnable() {
  307. public void run() {
  308. mAudioTrack.play();
  309. GodotLib.audio();
  310. }
  311. });
  312. // I'd take REALTIME if I could get it!
  313. mAudioThread.setPriority(Thread.MAX_PRIORITY);
  314. mAudioThread.start();
  315. }
  316. public void audioWriteShortBuffer(short[] buffer) {
  317. for (int i = 0; i < buffer.length;) {
  318. int result = mAudioTrack.write(buffer, i, buffer.length - i);
  319. if (result > 0) {
  320. i += result;
  321. } else if (result == 0) {
  322. try {
  323. Thread.sleep(1);
  324. } catch (InterruptedException e) {
  325. // Nom nom
  326. }
  327. } else {
  328. Log.w("Godot", "Godot audio: error return from write(short)");
  329. return;
  330. }
  331. }
  332. }
  333. public void audioQuit() {
  334. if (mAudioThread != null) {
  335. try {
  336. mAudioThread.join();
  337. } catch (Exception e) {
  338. Log.v("Godot", "Problem stopping audio thread: " + e);
  339. }
  340. mAudioThread = null;
  341. //Log.v("Godot", "Finished waiting for audio thread");
  342. }
  343. if (mAudioTrack != null) {
  344. mAudioTrack.stop();
  345. mAudioTrack = null;
  346. }
  347. }
  348. public void audioPause(boolean p_pause) {
  349. if (p_pause)
  350. mAudioTrack.pause();
  351. else
  352. mAudioTrack.play();
  353. }
  354. /////////////////////////
  355. // MISCELLANEOUS OS IO
  356. /////////////////////////
  357. public int openURI(String p_uri) {
  358. try {
  359. Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri);
  360. String path = p_uri;
  361. String type = "";
  362. if (path.startsWith("/")) {
  363. //absolute path to filesystem, prepend file://
  364. path = "file://" + path;
  365. if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) {
  366. type = "image/*";
  367. }
  368. }
  369. Intent intent = new Intent();
  370. intent.setAction(Intent.ACTION_VIEW);
  371. if (!type.equals("")) {
  372. intent.setDataAndType(Uri.parse(path), type);
  373. } else {
  374. intent.setData(Uri.parse(path));
  375. }
  376. activity.startActivity(intent);
  377. return 0;
  378. } catch (ActivityNotFoundException e) {
  379. return 1;
  380. }
  381. }
  382. public String getDataDir() {
  383. return activity.getFilesDir().getAbsolutePath();
  384. }
  385. public String getLocale() {
  386. return Locale.getDefault().toString();
  387. }
  388. public String getModel() {
  389. return Build.MODEL;
  390. }
  391. public int getScreenDPI() {
  392. DisplayMetrics metrics = activity.getApplicationContext().getResources().getDisplayMetrics();
  393. return (int)(metrics.density * 160f);
  394. }
  395. public void showKeyboard(String p_existing_text) {
  396. if (edit != null)
  397. edit.showKeyboard(p_existing_text);
  398. //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
  399. //inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
  400. };
  401. public void hideKeyboard() {
  402. if (edit != null)
  403. edit.hideKeyboard();
  404. };
  405. public void setScreenOrientation(int p_orientation) {
  406. switch (p_orientation) {
  407. case SCREEN_LANDSCAPE: {
  408. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  409. } break;
  410. case SCREEN_PORTRAIT: {
  411. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  412. } break;
  413. case SCREEN_REVERSE_LANDSCAPE: {
  414. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
  415. } break;
  416. case SCREEN_REVERSE_PORTRAIT: {
  417. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
  418. } break;
  419. case SCREEN_SENSOR_LANDSCAPE: {
  420. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
  421. } break;
  422. case SCREEN_SENSOR_PORTRAIT: {
  423. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
  424. } break;
  425. case SCREEN_SENSOR: {
  426. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
  427. } break;
  428. }
  429. };
  430. public void setEdit(GodotEditText _edit) {
  431. edit = _edit;
  432. }
  433. public void playVideo(String p_path) {
  434. Uri filePath = Uri.parse(p_path);
  435. mediaPlayer = new MediaPlayer();
  436. try {
  437. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  438. mediaPlayer.setDataSource(activity.getApplicationContext(), filePath);
  439. mediaPlayer.prepare();
  440. mediaPlayer.start();
  441. } catch (IOException e) {
  442. System.out.println("IOError while playing video");
  443. }
  444. }
  445. public boolean isVideoPlaying() {
  446. if (mediaPlayer != null) {
  447. return mediaPlayer.isPlaying();
  448. }
  449. return false;
  450. }
  451. public void pauseVideo() {
  452. if (mediaPlayer != null) {
  453. mediaPlayer.pause();
  454. }
  455. }
  456. public void stopVideo() {
  457. if (mediaPlayer != null) {
  458. mediaPlayer.release();
  459. mediaPlayer = null;
  460. }
  461. }
  462. public static final int SYSTEM_DIR_DESKTOP = 0;
  463. public static final int SYSTEM_DIR_DCIM = 1;
  464. public static final int SYSTEM_DIR_DOCUMENTS = 2;
  465. public static final int SYSTEM_DIR_DOWNLOADS = 3;
  466. public static final int SYSTEM_DIR_MOVIES = 4;
  467. public static final int SYSTEM_DIR_MUSIC = 5;
  468. public static final int SYSTEM_DIR_PICTURES = 6;
  469. public static final int SYSTEM_DIR_RINGTONES = 7;
  470. public String getSystemDir(int idx) {
  471. String what = "";
  472. switch (idx) {
  473. case SYSTEM_DIR_DESKTOP: {
  474. //what=Environment.DIRECTORY_DOCUMENTS;
  475. what = Environment.DIRECTORY_DOWNLOADS;
  476. } break;
  477. case SYSTEM_DIR_DCIM: {
  478. what = Environment.DIRECTORY_DCIM;
  479. } break;
  480. case SYSTEM_DIR_DOCUMENTS: {
  481. what = Environment.DIRECTORY_DOWNLOADS;
  482. //what=Environment.DIRECTORY_DOCUMENTS;
  483. } break;
  484. case SYSTEM_DIR_DOWNLOADS: {
  485. what = Environment.DIRECTORY_DOWNLOADS;
  486. } break;
  487. case SYSTEM_DIR_MOVIES: {
  488. what = Environment.DIRECTORY_MOVIES;
  489. } break;
  490. case SYSTEM_DIR_MUSIC: {
  491. what = Environment.DIRECTORY_MUSIC;
  492. } break;
  493. case SYSTEM_DIR_PICTURES: {
  494. what = Environment.DIRECTORY_PICTURES;
  495. } break;
  496. case SYSTEM_DIR_RINGTONES: {
  497. what = Environment.DIRECTORY_RINGTONES;
  498. } break;
  499. }
  500. if (what.equals(""))
  501. return "";
  502. return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath();
  503. }
  504. protected static final String PREFS_FILE = "device_id.xml";
  505. protected static final String PREFS_DEVICE_ID = "device_id";
  506. public static String unique_id = "";
  507. public String getUniqueID() {
  508. return unique_id;
  509. }
  510. }