package com.jme3.app;\r
\r
+import java.util.logging.Handler;\r
+import java.util.logging.Level;\r
import java.util.logging.Logger;\r
-\r
+import java.util.logging.SimpleFormatter;\r
+import com.jme3.util.JmeFormatter;\r
import android.app.Activity;\r
import android.app.AlertDialog;\r
import android.content.DialogInterface;\r
+import android.content.pm.ActivityInfo;\r
import android.opengl.GLSurfaceView;\r
import android.os.Bundle;\r
+import android.view.Display;\r
+import android.view.SurfaceView;\r
+import android.view.View;\r
import android.view.Window;\r
import android.view.WindowManager;\r
+import android.widget.TextView;\r
\r
import com.jme3.app.Application;\r
import com.jme3.input.TouchInput;\r
import com.jme3.system.AppSettings;\r
import com.jme3.system.JmeSystem;\r
import com.jme3.system.android.OGLESContext;\r
-\r
+import com.jme3.system.android.AndroidConfigChooser.ConfigType;\r
\r
/**\r
* <code>AndroidHarness</code> wraps a jme application object and runs it on Android\r
* @author Kirill\r
* @author larynx\r
*/\r
-public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener\r
-{\r
+public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener {\r
+\r
protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());\r
- \r
- protected OGLESContext ctx;\r
- protected GLSurfaceView view;\r
- \r
+ /**\r
+ * The application class to start\r
+ */\r
protected String appClass = "jme3test.android.Test";\r
+ /**\r
+ * The jme3 application object\r
+ */\r
protected Application app = null;\r
+ /**\r
+ * ConfigType.FASTEST is RGB565, GLSurfaceView default\r
+ * ConfigType.BEST is RGBA8888 or better if supported by the hardware\r
+ */\r
+ protected ConfigType eglConfigType = ConfigType.FASTEST;\r
+ /**\r
+ * If true all valid and not valid egl configs are logged\r
+ */\r
+ protected boolean eglConfigVerboseLogging = false;\r
+ /**\r
+ * If true MouseEvents are generated from TouchEvents\r
+ */\r
+ protected boolean mouseEventsEnabled = true;\r
+ /**\r
+ * Flip X axis\r
+ */\r
+ protected boolean mouseEventsInvertX = true;\r
+ /**\r
+ * Flip Y axis\r
+ */\r
+ protected boolean mouseEventsInvertY = true;\r
+ /**\r
+ * Title of the exit dialog, default is "Do you want to exit?"\r
+ */\r
+ protected String exitDialogTitle = "Do you want to exit?";\r
+ /**\r
+ * Message of the exit dialog, default is "Use your home key to bring this app into the background or exit to terminate it."\r
+ */\r
+ protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";\r
+ \r
+ /**\r
+ * Set the screen window size\r
+ * if screenFullSize is true, then the notification bar and title bar are\r
+ * removed and the screen covers the entire display\r
+ * if screenFullSize is false, then the notification bar remains visible\r
+ * if screenShowTitle is true while screenFullScreen is false, then the\r
+ * title bar is also displayed under the notification bar\r
+ */\r
+ protected boolean screenFullScreen = true;\r
+ \r
+ /**\r
+ * if screenShowTitle is true while screenFullScreen is false, then the\r
+ * title bar is also displayed under the notification bar\r
+ */\r
+ protected boolean screenShowTitle = true;\r
\r
- protected boolean debug = false; \r
+ /**\r
+ * Set the screen orientation, default is SENSOR\r
+ * ActivityInfo.SCREEN_ORIENTATION_* constants\r
+ * package android.content.pm.ActivityInfo\r
+ * \r
+ * SCREEN_ORIENTATION_UNSPECIFIED\r
+ * SCREEN_ORIENTATION_LANDSCAPE\r
+ * SCREEN_ORIENTATION_PORTRAIT\r
+ * SCREEN_ORIENTATION_USER\r
+ * SCREEN_ORIENTATION_BEHIND\r
+ * SCREEN_ORIENTATION_SENSOR (default)\r
+ * SCREEN_ORIENTATION_NOSENSOR\r
+ */\r
+ protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;\r
+ protected OGLESContext ctx;\r
+ protected GLSurfaceView view = null;\r
+ protected boolean isGLThreadPaused = true;\r
final private String ESCAPE_EVENT = "TouchEscape";\r
\r
@Override\r
- public void onCreate(Bundle savedInstanceState) \r
- {\r
+ public void onCreate(Bundle savedInstanceState) {\r
super.onCreate(savedInstanceState);\r
\r
+ Logger log = logger;\r
+ boolean bIsLogFormatSet = false;\r
+ do {\r
+ if (log.getHandlers().length == 0) {\r
+ log = logger.getParent();\r
+ if (log != null) {\r
+ for (Handler h : log.getHandlers()) {\r
+ //h.setFormatter(new SimpleFormatter());\r
+ h.setFormatter(new JmeFormatter());\r
+ bIsLogFormatSet = true;\r
+ }\r
+ }\r
+ }\r
+ } while (log != null && !bIsLogFormatSet);\r
+\r
JmeSystem.setResources(getResources());\r
+ JmeSystem.setActivity(this);\r
+\r
+ if (screenFullScreen) {\r
+ requestWindowFeature(Window.FEATURE_NO_TITLE);\r
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,\r
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);\r
+ } else {\r
+ if (!screenShowTitle) {\r
+ requestWindowFeature(Window.FEATURE_NO_TITLE);\r
+ }\r
+ }\r
\r
- requestWindowFeature(Window.FEATURE_NO_TITLE);\r
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,\r
- WindowManager.LayoutParams.FLAG_FULLSCREEN);\r
+ setRequestedOrientation(screenOrientation);\r
\r
+ // Create Settings\r
AppSettings settings = new AppSettings(true);\r
+\r
+ // Create the input class\r
AndroidInput input = new AndroidInput(this);\r
- \r
+ input.setMouseEventsInvertX(mouseEventsInvertX);\r
+ input.setMouseEventsInvertY(mouseEventsInvertY);\r
+ input.setMouseEventsEnabled(mouseEventsEnabled);\r
+\r
// Create application instance\r
- try{\r
- @SuppressWarnings("unchecked")\r
- Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);\r
- app = clazz.newInstance();\r
- }catch (Exception ex){\r
- handleError("Class " + appClass + " init failed", ex);\r
- }\r
+ try {\r
+ if (app == null) {\r
+ @SuppressWarnings("unchecked")\r
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);\r
+ app = clazz.newInstance();\r
+ }\r
\r
- app.setSettings(settings);\r
- app.start(); \r
- ctx = (OGLESContext) app.getContext();\r
- if (debug)\r
- {\r
- view = ctx.createView(input, GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS);\r
- }\r
- else\r
- {\r
- view = ctx.createView(input);\r
+ app.setSettings(settings);\r
+ app.start();\r
+ ctx = (OGLESContext) app.getContext();\r
+ view = ctx.createView(input, eglConfigType, eglConfigVerboseLogging);\r
+ setContentView(view);\r
+\r
+ // Set the screen reolution\r
+ WindowManager wind = this.getWindowManager();\r
+ Display disp = wind.getDefaultDisplay();\r
+ ctx.getSettings().setResolution(disp.getWidth(), disp.getHeight());\r
+\r
+ AppSettings s = ctx.getSettings();\r
+ logger.log(Level.INFO, "Settings: Width {0} Height {1}", new Object[]{s.getWidth(), s.getHeight()});\r
+ } catch (Exception ex) {\r
+ handleError("Class " + appClass + " init failed", ex);\r
+ setContentView(new TextView(this));\r
}\r
- setContentView(view); \r
}\r
\r
-\r
@Override\r
- protected void onRestart(){\r
- super.onRestart(); \r
- app.restart();\r
+ protected void onRestart() {\r
+ super.onRestart();\r
+ if (app != null) {\r
+ app.restart();\r
+ }\r
logger.info("onRestart");\r
}\r
- \r
\r
@Override\r
- protected void onStart(){\r
+ protected void onStart() {\r
super.onStart();\r
logger.info("onStart");\r
}\r
- \r
+\r
@Override\r
protected void onResume() {\r
super.onResume();\r
- view.onResume();\r
+ if (view != null) {\r
+ view.onResume();\r
+ }\r
+ isGLThreadPaused = false;\r
logger.info("onResume");\r
}\r
\r
@Override\r
protected void onPause() {\r
super.onPause();\r
- view.onPause();\r
+ if (view != null) {\r
+ view.onPause();\r
+ }\r
+ isGLThreadPaused = true;\r
logger.info("onPause");\r
}\r
- \r
+\r
@Override\r
- protected void onStop(){\r
+ protected void onStop() {\r
super.onStop();\r
logger.info("onStop");\r
}\r
\r
@Override\r
- protected void onDestroy(){\r
- super.onDestroy(); \r
- app.stop();\r
+ protected void onDestroy() {\r
+ if (app != null) {\r
+ app.stop(!isGLThreadPaused);\r
+ }\r
+ super.onDestroy();\r
logger.info("onDestroy");\r
}\r
\r
- public Application getJmeApplication()\r
- {\r
+ public Application getJmeApplication() {\r
return app;\r
}\r
+\r
/**\r
* Called when an error has occured. This is typically\r
- * invoked when an uncought exception is thrown in the render thread.\r
+ * invoked when an uncaught exception is thrown in the render thread.\r
* @param errorMsg The error message, if any, or null.\r
* @param t Throwable object, or null.\r
*/\r
- public void handleError(final String errorMsg, final Throwable t)\r
- {\r
- \r
- String s = "";\r
- if (t != null && t.getStackTrace() != null)\r
- {\r
- for (StackTraceElement ste: t.getStackTrace())\r
- {\r
- s += ste.getClassName() + "." + ste.getMethodName() + "(" + + ste.getLineNumber() + ") ";\r
+ public void handleError(final String errorMsg, final Throwable t) {\r
+ String sTrace = "";\r
+ if (t != null && t.getStackTrace() != null) {\r
+ for (StackTraceElement ste : t.getStackTrace()) {\r
+ sTrace += "\tat " + ste.getClassName() + "." + ste.getMethodName() + "(";\r
+ if (ste.isNativeMethod()){\r
+ sTrace += "Native";\r
+ }else{\r
+ sTrace += ste.getLineNumber();\r
+ }\r
+ sTrace += ")\n";\r
}\r
- } \r
- \r
- final String sTrace = s;\r
- \r
- logger.severe(t != null ? t.toString() : "OpenGL Exception");\r
- logger.severe((errorMsg != null ? errorMsg + ": " : "") + sTrace);\r
- \r
+ }\r
+\r
+ final String stackTrace = sTrace;\r
+\r
+ logger.log(Level.SEVERE, t != null ? t.toString() : "OpenGL Exception");\r
+ logger.log(Level.SEVERE, "{0}{1}", new Object[]{errorMsg != null ? errorMsg + ": " : "", stackTrace});\r
+\r
this.runOnUiThread(new Runnable() {\r
@Override\r
- public void run() \r
- { \r
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this)\r
- // .setIcon(R.drawable.alert_dialog_icon)\r
- .setTitle(t != null ? (t.getMessage() != null ? (t.getMessage() + ": " + t.getClass().getName()) : t.getClass().getName()) : "OpenGL Exception")\r
- .setPositiveButton("Kill", AndroidHarness.this)\r
- .setMessage((errorMsg != null ? errorMsg + ": " : "") + sTrace)\r
- .create(); \r
- dialog.show(); \r
+ public void run() {\r
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)\r
+ .setTitle(t != null ? (t.getMessage() != null ? (t.getMessage() + ": " + t.getClass().getName()) : t.getClass().getName()) : "OpenGL Exception").setPositiveButton("Kill", AndroidHarness.this).setMessage(\r
+ (t != null ? (t.getMessage() != null ? (t.getMessage() + ": " + t.getClass().getName()) : t.getClass().getName()) : "OpenGL Exception")\r
+ +"´n"+(errorMsg != null ? errorMsg + ": " : "") + stackTrace).create();\r
+ dialog.show();\r
}\r
});\r
- \r
+\r
}\r
- \r
+\r
/**\r
* Called by the android alert dialog, terminate the activity and OpenGL rendering\r
* @param dialog\r
* @param whichButton\r
*/\r
- public void onClick(DialogInterface dialog, int whichButton) \r
- { \r
- if (whichButton != -2)\r
- {\r
- app.stop();\r
+ public void onClick(DialogInterface dialog, int whichButton) {\r
+ if (whichButton != -2) {\r
+ android.os.Process.killProcess(android.os.Process.myPid());\r
+ if (app != null) {\r
+ app.stop(true);\r
+ }\r
this.finish();\r
}\r
}\r
- \r
+\r
/**\r
* Gets called by the InputManager on all touch/drag/scale events\r
- */ \r
- @Override \r
- public void onTouch(String name, TouchEvent evt, float tpf) \r
- {\r
- if (name.equals(ESCAPE_EVENT))\r
- {\r
- switch(evt.getType())\r
- { \r
+ */\r
+ @Override\r
+ public void onTouch(String name, TouchEvent evt, float tpf) {\r
+ if (name.equals(ESCAPE_EVENT)) {\r
+ switch (evt.getType()) {\r
case KEY_UP:\r
- this.runOnUiThread(new Runnable() \r
- {\r
+ this.runOnUiThread(new Runnable() {\r
@Override\r
- public void run() \r
- { \r
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this)\r
- // .setIcon(R.drawable.alert_dialog_icon)\r
- .setTitle("Do you want to exit?")\r
- .setPositiveButton("Yes", AndroidHarness.this)\r
- .setNegativeButton("No", AndroidHarness.this)\r
- .setMessage("Use your home key to bring this app into the background or exit to terminate it.")\r
- .create(); \r
- dialog.show(); \r
+ public void run() {\r
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)\r
+ .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();\r
+ dialog.show();\r
}\r
});\r
- \r
- \r
+\r
+ break;\r
+ default:\r
break;\r
- \r
- default:\r
- break;\r
}\r
}\r
- \r
- } \r
- \r
+\r
+ }\r
}\r