AndroidHarness.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. package com.jme3.app;
  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.content.DialogInterface;
  5. import android.content.pm.ActivityInfo;
  6. import android.graphics.drawable.Drawable;
  7. import android.graphics.drawable.NinePatchDrawable;
  8. import android.opengl.GLSurfaceView;
  9. import android.os.Bundle;
  10. import android.view.Display;
  11. import android.view.Gravity;
  12. import android.view.View;
  13. import android.view.ViewGroup.LayoutParams;
  14. import android.view.Window;
  15. import android.view.WindowManager;
  16. import android.widget.FrameLayout;
  17. import android.widget.ImageView;
  18. import android.widget.TextView;
  19. import com.jme3.input.android.AndroidInput;
  20. import com.jme3.input.controls.TouchListener;
  21. import com.jme3.input.event.TouchEvent;
  22. import com.jme3.system.AppSettings;
  23. import com.jme3.system.JmeSystem;
  24. import com.jme3.system.android.AndroidConfigChooser.ConfigType;
  25. import com.jme3.system.android.JmeAndroidSystem;
  26. import com.jme3.system.android.OGLESContext;
  27. import com.jme3.util.JmeFormatter;
  28. import java.io.PrintStream;
  29. import java.io.PrintWriter;
  30. import java.io.StringWriter;
  31. import java.util.logging.Handler;
  32. import java.util.logging.Level;
  33. import java.util.logging.Logger;
  34. /**
  35. * <code>AndroidHarness</code> wraps a jme application object and runs it on
  36. * Android
  37. *
  38. * @author Kirill
  39. * @author larynx
  40. */
  41. public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener {
  42. protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
  43. /**
  44. * The application class to start
  45. */
  46. protected String appClass = "jme3test.android.Test";
  47. /**
  48. * The jme3 application object
  49. */
  50. protected Application app = null;
  51. /**
  52. * ConfigType.FASTEST is RGB565, GLSurfaceView default ConfigType.BEST is
  53. * RGBA8888 or better if supported by the hardware
  54. */
  55. protected ConfigType eglConfigType = ConfigType.FASTEST;
  56. /**
  57. * If true all valid and not valid egl configs are logged
  58. */
  59. protected boolean eglConfigVerboseLogging = false;
  60. /**
  61. * If true MouseEvents are generated from TouchEvents
  62. */
  63. protected boolean mouseEventsEnabled = true;
  64. /**
  65. * Flip X axis
  66. */
  67. protected boolean mouseEventsInvertX = true;
  68. /**
  69. * Flip Y axis
  70. */
  71. protected boolean mouseEventsInvertY = true;
  72. /**
  73. * Title of the exit dialog, default is "Do you want to exit?"
  74. */
  75. protected String exitDialogTitle = "Do you want to exit?";
  76. /**
  77. * Message of the exit dialog, default is "Use your home key to bring this
  78. * app into the background or exit to terminate it."
  79. */
  80. protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
  81. /**
  82. * Set the screen window mode.
  83. * If screenFullSize is true, then the notification bar and title bar are
  84. * removed and the screen covers the entire display.  
  85. * If screenFullSize is false, then the notification bar remains visible if
  86. * screenShowTitle is true while screenFullScreen is false,
  87. * then the title bar is also displayed under the notification bar.
  88. */
  89. protected boolean screenFullScreen = true;
  90. /**
  91. * if screenShowTitle is true while screenFullScreen is false, then the
  92. * title bar is also displayed under the notification bar
  93. */
  94. protected boolean screenShowTitle = true;
  95. /**
  96. * Splash Screen picture Resource ID. If a Splash Screen is desired, set
  97. * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
  98. * splashPicID = 0, then no splash screen will be displayed.
  99. */
  100. protected int splashPicID = 0;
  101. /**
  102. * Set the screen orientation, default is SENSOR
  103. * ActivityInfo.SCREEN_ORIENTATION_* constants package
  104. * android.content.pm.ActivityInfo
  105. *
  106. * SCREEN_ORIENTATION_UNSPECIFIED SCREEN_ORIENTATION_LANDSCAPE
  107. * SCREEN_ORIENTATION_PORTRAIT SCREEN_ORIENTATION_USER
  108. * SCREEN_ORIENTATION_BEHIND SCREEN_ORIENTATION_SENSOR (default)
  109. * SCREEN_ORIENTATION_NOSENSOR
  110. */
  111. protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
  112. protected OGLESContext ctx;
  113. protected GLSurfaceView view = null;
  114. protected boolean isGLThreadPaused = true;
  115. private ImageView splashImageView = null;
  116. private FrameLayout frameLayout = null;
  117. final private String ESCAPE_EVENT = "TouchEscape";
  118. static {
  119. try {
  120. System.loadLibrary("bulletjme");
  121. } catch (UnsatisfiedLinkError e) {
  122. }
  123. JmeSystem.setSystemDelegate(new JmeAndroidSystem());
  124. }
  125. @Override
  126. public void onCreate(Bundle savedInstanceState) {
  127. super.onCreate(savedInstanceState);
  128. Logger log = logger;
  129. boolean bIsLogFormatSet = false;
  130. do {
  131. if (log.getHandlers().length == 0) {
  132. log = logger.getParent();
  133. if (log != null) {
  134. for (Handler h : log.getHandlers()) {
  135. //h.setFormatter(new SimpleFormatter());
  136. h.setFormatter(new JmeFormatter());
  137. bIsLogFormatSet = true;
  138. }
  139. }
  140. }
  141. } while (log != null && !bIsLogFormatSet);
  142. JmeAndroidSystem.setResources(getResources());
  143. JmeAndroidSystem.setActivity(this);
  144. if (screenFullScreen) {
  145. requestWindowFeature(Window.FEATURE_NO_TITLE);
  146. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  147. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  148. } else {
  149. if (!screenShowTitle) {
  150. requestWindowFeature(Window.FEATURE_NO_TITLE);
  151. }
  152. }
  153. setRequestedOrientation(screenOrientation);
  154. // Create Settings
  155. AppSettings settings = new AppSettings(true);
  156. // Create the input class
  157. AndroidInput input = new AndroidInput(this);
  158. input.setMouseEventsInvertX(mouseEventsInvertX);
  159. input.setMouseEventsInvertY(mouseEventsInvertY);
  160. input.setMouseEventsEnabled(mouseEventsEnabled);
  161. // Create application instance
  162. try {
  163. if (app == null) {
  164. @SuppressWarnings("unchecked")
  165. Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
  166. app = clazz.newInstance();
  167. }
  168. app.setSettings(settings);
  169. app.start();
  170. ctx = (OGLESContext) app.getContext();
  171. view = ctx.createView(input, eglConfigType, eglConfigVerboseLogging);
  172. // Set the screen reolution
  173. WindowManager wind = this.getWindowManager();
  174. Display disp = wind.getDefaultDisplay();
  175. ctx.getSettings().setResolution(disp.getWidth(), disp.getHeight());
  176. AppSettings s = ctx.getSettings();
  177. logger.log(Level.INFO, "Settings: Width {0} Height {1}", new Object[]{s.getWidth(), s.getHeight()});
  178. layoutDisplay();
  179. } catch (Exception ex) {
  180. handleError("Class " + appClass + " init failed", ex);
  181. setContentView(new TextView(this));
  182. }
  183. }
  184. @Override
  185. protected void onRestart() {
  186. super.onRestart();
  187. if (app != null) {
  188. app.restart();
  189. }
  190. logger.info("onRestart");
  191. }
  192. @Override
  193. protected void onStart() {
  194. super.onStart();
  195. logger.info("onStart");
  196. }
  197. @Override
  198. protected void onResume() {
  199. super.onResume();
  200. if (view != null) {
  201. view.onResume();
  202. }
  203. isGLThreadPaused = false;
  204. logger.info("onResume");
  205. }
  206. @Override
  207. protected void onPause() {
  208. super.onPause();
  209. if (view != null) {
  210. view.onPause();
  211. }
  212. isGLThreadPaused = true;
  213. logger.info("onPause");
  214. }
  215. @Override
  216. protected void onStop() {
  217. super.onStop();
  218. logger.info("onStop");
  219. }
  220. @Override
  221. protected void onDestroy() {
  222. if (app != null) {
  223. app.stop(!isGLThreadPaused);
  224. }
  225. logger.info("onDestroy");
  226. super.onDestroy();
  227. }
  228. public Application getJmeApplication() {
  229. return app;
  230. }
  231. /**
  232. * Called when an error has occurred.
  233. * By default, will show an error message to the user
  234. * and print the exception/error to the log.
  235. */
  236. public void handleError(final String errorMsg, final Throwable t) {
  237. String stackTrace = "";
  238. String title = "Error";
  239. if (t != null){
  240. // Convert exception to string
  241. StringWriter sw = new StringWriter(100);
  242. t.printStackTrace(new PrintWriter(sw));
  243. stackTrace = sw.toString();
  244. title = t.toString();
  245. }
  246. final String finalTitle = title;
  247. final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
  248. + "\n" + stackTrace;
  249. logger.log(Level.SEVERE, finalMsg);
  250. runOnUiThread(new Runnable() {
  251. @Override
  252. public void run() {
  253. AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
  254. .setTitle(finalTitle)
  255. .setPositiveButton("Kill", AndroidHarness.this)
  256. .setMessage(finalMsg).create();
  257. dialog.show();
  258. }
  259. });
  260. }
  261. /**
  262. * Called by the android alert dialog, terminate the activity and OpenGL
  263. * rendering
  264. *
  265. * @param dialog
  266. * @param whichButton
  267. */
  268. public void onClick(DialogInterface dialog, int whichButton) {
  269. if (whichButton != -2) {
  270. if (app != null) {
  271. app.stop(true);
  272. }
  273. this.finish();
  274. }
  275. }
  276. /**
  277. * Gets called by the InputManager on all touch/drag/scale events
  278. */
  279. @Override
  280. public void onTouch(String name, TouchEvent evt, float tpf) {
  281. if (name.equals(ESCAPE_EVENT)) {
  282. switch (evt.getType()) {
  283. case KEY_UP:
  284. runOnUiThread(new Runnable() {
  285. @Override
  286. public void run() {
  287. AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
  288. .setTitle(exitDialogTitle)
  289. .setPositiveButton("Yes", AndroidHarness.this)
  290. .setNegativeButton("No", AndroidHarness.this)
  291. .setMessage(exitDialogMessage).create();
  292. dialog.show();
  293. }
  294. });
  295. break;
  296. default:
  297. break;
  298. }
  299. }
  300. }
  301. public void layoutDisplay() {
  302. logger.log(Level.INFO, "Splash Screen Picture Resource ID: {0}", splashPicID);
  303. if (splashPicID != 0) {
  304. FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
  305. LayoutParams.FILL_PARENT,
  306. LayoutParams.FILL_PARENT,
  307. Gravity.CENTER);
  308. frameLayout = new FrameLayout(this);
  309. splashImageView = new ImageView(this);
  310. Drawable drawable = this.getResources().getDrawable(splashPicID);
  311. if (drawable instanceof NinePatchDrawable) {
  312. splashImageView.setBackgroundDrawable(drawable);
  313. } else {
  314. splashImageView.setImageResource(splashPicID);
  315. }
  316. frameLayout.addView(view);
  317. frameLayout.addView(splashImageView, lp);
  318. setContentView(frameLayout);
  319. logger.log(Level.INFO, "Splash Screen Created");
  320. } else {
  321. logger.log(Level.INFO, "Splash Screen Skipped.");
  322. setContentView(view);
  323. }
  324. }
  325. public void removeSplashScreen() {
  326. logger.log(Level.INFO, "Splash Screen Picture Resource ID: {0}", splashPicID);
  327. if (splashPicID != 0) {
  328. if (frameLayout != null) {
  329. if (splashImageView != null) {
  330. this.runOnUiThread(new Runnable() {
  331. @Override
  332. public void run() {
  333. splashImageView.setVisibility(View.INVISIBLE);
  334. frameLayout.removeView(splashImageView);
  335. }
  336. });
  337. } else {
  338. logger.log(Level.INFO, "splashImageView is null");
  339. }
  340. } else {
  341. logger.log(Level.INFO, "frameLayout is null");
  342. }
  343. }
  344. }
  345. }