diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
index 1d2835bc3..b103e92df 100644
--- a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
+++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
@@ -1,586 +1,580 @@
-package com.jme3.app;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.*;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-import com.jme3.audio.AudioRenderer;
-import com.jme3.input.JoyInput;
-import com.jme3.input.TouchInput;
-import com.jme3.input.android.AndroidSensorJoyInput;
-import com.jme3.input.controls.TouchListener;
-import com.jme3.input.controls.TouchTrigger;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.system.AppSettings;
-import com.jme3.system.SystemListener;
-import com.jme3.system.android.JmeAndroidSystem;
-import com.jme3.system.android.OGLESContext;
-import com.jme3.util.AndroidLogHandler;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-/**
- * AndroidHarness wraps a jme application object and runs it on
- * Android
- *
- * @author Kirill
- * @author larynx
- */
-public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener {
-
- protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
- /**
- * The application class to start
- */
- protected String appClass = "jme3test.android.Test";
- /**
- * The jme3 application object
- */
- protected Application app = null;
-
- /**
- * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
- * (default = 24)
- */
- protected int eglBitsPerPixel = 24;
-
- /**
- * Sets the desired number of Alpha bits for the surfaceview. This affects
- * how the surfaceview is able to display Android views that are located
- * under the surfaceview jME uses to render the scenegraph.
- * 0 = Opaque surfaceview background (fastest)
- * 1->7 = Transparent surfaceview background
- * 8 or higher = Translucent surfaceview background
- * (default = 0)
- */
- protected int eglAlphaBits = 0;
-
- /**
- * The number of depth bits specifies the precision of the depth buffer.
- * (default = 16)
- */
- protected int eglDepthBits = 16;
-
- /**
- * Sets the number of samples to use for multisampling.
- * Leave 0 (default) to disable multisampling.
- * Set to 2 or 4 to enable multisampling.
- */
- protected int eglSamples = 0;
-
- /**
- * Set the number of stencil bits.
- * (default = 0)
- */
- protected int eglStencilBits = 0;
-
- /**
- * Set the desired frame rate. If frameRate > 0, the application
- * will be capped at the desired frame rate.
- * (default = -1, no frame rate cap)
- */
- protected int frameRate = -1;
-
- /**
- * Sets the type of Audio Renderer to be used.
- *
- * Android MediaPlayer / SoundPool can be used on all
- * supported Android platform versions (2.2+)
- * OpenAL Soft uses an OpenSL backend and is only supported on Android
- * versions 2.3+.
- *
- * Only use ANDROID_ static strings found in AppSettings
- *
- */
- protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
-
- /**
- * If true Android Sensors are used as simulated Joysticks. Users can use the
- * Android sensor feedback through the RawInputListener or by registering
- * JoyAxisTriggers.
- */
- protected boolean joystickEventsEnabled = false;
- /**
- * If true KeyEvents are generated from TouchEvents
- */
- protected boolean keyEventsEnabled = true;
- /**
- * If true MouseEvents are generated from TouchEvents
- */
- protected boolean mouseEventsEnabled = true;
- /**
- * Flip X axis
- */
- protected boolean mouseEventsInvertX = false;
- /**
- * Flip Y axis
- */
- protected boolean mouseEventsInvertY = false;
- /**
- * if true finish this activity when the jme app is stopped
- */
- protected boolean finishOnAppStop = true;
- /**
- * set to false if you don't want the harness to handle the exit hook
- */
- protected boolean handleExitHook = true;
- /**
- * Title of the exit dialog, default is "Do you want to exit?"
- */
- protected String exitDialogTitle = "Do you want to exit?";
- /**
- * Message of the exit dialog, default is "Use your home key to bring this
- * app into the background or exit to terminate it."
- */
- protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
- /**
- * Set the screen window mode. If screenFullSize is true, then the
- * notification bar and title bar are removed and the screen covers the
- * entire display. If screenFullSize is false, then the notification bar
- * remains visible if screenShowTitle is true while screenFullScreen is
- * false, then the title bar is also displayed under the notification bar.
- */
- protected boolean screenFullScreen = true;
- /**
- * if screenShowTitle is true while screenFullScreen is false, then the
- * title bar is also displayed under the notification bar
- */
- protected boolean screenShowTitle = true;
- /**
- * Splash Screen picture Resource ID. If a Splash Screen is desired, set
- * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
- * splashPicID = 0, then no splash screen will be displayed.
- */
- protected int splashPicID = 0;
-
- /**
- * No longer used - Use the android:screenOrientation declaration in
- * the AndroidManifest.xml file.
- */
- @Deprecated
- protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
-
- protected OGLESContext ctx;
- protected GLSurfaceView view = null;
- protected boolean isGLThreadPaused = true;
- protected ImageView splashImageView = null;
- protected FrameLayout frameLayout = null;
- final private String ESCAPE_EVENT = "TouchEscape";
- private boolean firstDrawFrame = true;
- private boolean inConfigChange = false;
-
- private class DataObject {
- protected Application app = null;
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- logger.log(Level.FINE, "onRetainNonConfigurationInstance");
- final DataObject data = new DataObject();
- data.app = this.app;
- inConfigChange = true;
- return data;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- initializeLogHandler();
-
- logger.fine("onCreate");
- super.onCreate(savedInstanceState);
-
- if (screenFullScreen) {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- } else {
- if (!screenShowTitle) {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- }
- }
-
- final DataObject data = (DataObject) getLastNonConfigurationInstance();
- if (data != null) {
- logger.log(Level.FINE, "Using Retained App");
- this.app = data.app;
- } else {
- // Discover the screen reolution
- //TODO try to find a better way to get a hand on the resolution
- WindowManager wind = this.getWindowManager();
- Display disp = wind.getDefaultDisplay();
- Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight());
-
- // Create Settings
- logger.log(Level.FINE, "Creating settings");
- AppSettings settings = new AppSettings(true);
- settings.setEmulateMouse(mouseEventsEnabled);
- settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY);
- settings.setUseJoysticks(joystickEventsEnabled);
- settings.setEmulateKeyboard(keyEventsEnabled);
-
- settings.setBitsPerPixel(eglBitsPerPixel);
- settings.setAlphaBits(eglAlphaBits);
- settings.setDepthBits(eglDepthBits);
- settings.setSamples(eglSamples);
- settings.setStencilBits(eglStencilBits);
-
- settings.setResolution(disp.getWidth(), disp.getHeight());
- settings.setAudioRenderer(audioRendererType);
-
- settings.setFrameRate(frameRate);
-
- // Create application instance
- try {
- if (app == null) {
- @SuppressWarnings("unchecked")
- Class extends Application> clazz = (Class extends Application>) Class.forName(appClass);
- app = clazz.newInstance();
- }
-
- app.setSettings(settings);
- app.start();
- } catch (Exception ex) {
- handleError("Class " + appClass + " init failed", ex);
- setContentView(new TextView(this));
- }
- }
-
- ctx = (OGLESContext) app.getContext();
- view = ctx.createView(this);
- // store the glSurfaceView in JmeAndroidSystem for future use
- JmeAndroidSystem.setView(view);
- // AndroidHarness wraps the app as a SystemListener.
- ctx.setSystemListener(this);
- layoutDisplay();
- }
-
- @Override
- protected void onRestart() {
- logger.fine("onRestart");
- super.onRestart();
- if (app != null) {
- app.restart();
- }
- }
-
- @Override
- protected void onStart() {
- logger.fine("onStart");
- super.onStart();
- }
-
- @Override
- protected void onResume() {
- logger.fine("onResume");
- super.onResume();
-
- gainFocus();
- }
-
- @Override
- protected void onPause() {
- logger.fine("onPause");
- loseFocus();
-
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- logger.fine("onStop");
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- logger.fine("onDestroy");
- final DataObject data = (DataObject) getLastNonConfigurationInstance();
- if (data != null || inConfigChange) {
- logger.fine("In Config Change, not stopping app.");
- } else {
- if (app != null) {
- app.stop(!isGLThreadPaused);
- }
- }
- setContentView(new TextView(this));
- ctx = null;
- app = null;
- view = null;
- JmeAndroidSystem.setView(null);
-
- super.onDestroy();
- }
-
- public Application getJmeApplication() {
- return app;
- }
-
- /**
- * Called when an error has occurred. By default, will show an error message
- * to the user and print the exception/error to the log.
- */
- @Override
- public void handleError(final String errorMsg, final Throwable t) {
- String stackTrace = "";
- String title = "Error";
-
- if (t != null) {
- // Convert exception to string
- StringWriter sw = new StringWriter(100);
- t.printStackTrace(new PrintWriter(sw));
- stackTrace = sw.toString();
- title = t.toString();
- }
-
- final String finalTitle = title;
- final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
- + "\n" + stackTrace;
-
- logger.log(Level.SEVERE, finalMsg);
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
- .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create();
- dialog.show();
- }
- });
- }
-
- /**
- * Called by the android alert dialog, terminate the activity and OpenGL
- * rendering
- *
- * @param dialog
- * @param whichButton
- */
- public void onClick(DialogInterface dialog, int whichButton) {
- if (whichButton != -2) {
- if (app != null) {
- app.stop(true);
- }
- app = null;
- this.finish();
- }
- }
-
- /**
- * Gets called by the InputManager on all touch/drag/scale events
- */
- @Override
- public void onTouch(String name, TouchEvent evt, float tpf) {
- if (name.equals(ESCAPE_EVENT)) {
- switch (evt.getType()) {
- case KEY_UP:
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
- .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();
- dialog.show();
- }
- });
- break;
- default:
- break;
- }
- }
- }
-
- public void layoutDisplay() {
- logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
- if (view == null) {
- logger.log(Level.FINE, "view is null!");
- }
- if (splashPicID != 0) {
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT,
- Gravity.CENTER);
-
- frameLayout = new FrameLayout(this);
- splashImageView = new ImageView(this);
-
- Drawable drawable = this.getResources().getDrawable(splashPicID);
- if (drawable instanceof NinePatchDrawable) {
- splashImageView.setBackgroundDrawable(drawable);
- } else {
- splashImageView.setImageResource(splashPicID);
- }
-
- if (view.getParent() != null) {
- ((ViewGroup) view.getParent()).removeView(view);
- }
- frameLayout.addView(view);
-
- if (splashImageView.getParent() != null) {
- ((ViewGroup) splashImageView.getParent()).removeView(splashImageView);
- }
- frameLayout.addView(splashImageView, lp);
-
- setContentView(frameLayout);
- logger.log(Level.FINE, "Splash Screen Created");
- } else {
- logger.log(Level.FINE, "Splash Screen Skipped.");
- setContentView(view);
- }
- }
-
- public void removeSplashScreen() {
- logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
- if (splashPicID != 0) {
- if (frameLayout != null) {
- if (splashImageView != null) {
- this.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- splashImageView.setVisibility(View.INVISIBLE);
- frameLayout.removeView(splashImageView);
- }
- });
- } else {
- logger.log(Level.FINE, "splashImageView is null");
- }
- } else {
- logger.log(Level.FINE, "frameLayout is null");
- }
- }
- }
-
- /**
- * Removes the standard Android log handler due to an issue with not logging
- * entries lower than INFO level and adds a handler that produces
- * JME formatted log messages.
- */
- protected void initializeLogHandler() {
- Logger log = LogManager.getLogManager().getLogger("");
- for (Handler handler : log.getHandlers()) {
- if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) {
- Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName());
- }
- log.removeHandler(handler);
- }
- Handler handler = new AndroidLogHandler();
- log.addHandler(handler);
- handler.setLevel(Level.ALL);
- }
-
- public void initialize() {
- app.initialize();
- if (handleExitHook) {
- // remove existing mapping from SimpleApplication that stops the app
- // when the esc key is pressed (esc key = android back key) so that
- // AndroidHarness can produce the exit app dialog box.
- if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) {
- app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
- }
-
- app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
- app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT});
- }
- }
-
- public void reshape(int width, int height) {
- app.reshape(width, height);
- }
-
- public void update() {
- app.update();
- // call to remove the splash screen, if present.
- // call after app.update() to make sure no gap between
- // splash screen going away and app display being shown.
- if (firstDrawFrame) {
- removeSplashScreen();
- firstDrawFrame = false;
- }
- }
-
- public void requestClose(boolean esc) {
- app.requestClose(esc);
- }
-
- public void destroy() {
- if (app != null) {
- app.destroy();
- }
- if (finishOnAppStop) {
- finish();
- }
- }
-
- public void gainFocus() {
- logger.fine("gainFocus");
- if (view != null) {
- view.onResume();
- }
-
- if (app != null) {
- //resume the audio
- AudioRenderer audioRenderer = app.getAudioRenderer();
- if (audioRenderer != null) {
- audioRenderer.resumeAll();
- }
- //resume the sensors (aka joysticks)
- if (app.getContext() != null) {
- JoyInput joyInput = app.getContext().getJoyInput();
- if (joyInput != null) {
- if (joyInput instanceof AndroidSensorJoyInput) {
- AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
- androidJoyInput.resumeSensors();
- }
- }
- }
- }
-
- isGLThreadPaused = false;
-
- if (app != null) {
- app.gainFocus();
- }
- }
-
- public void loseFocus() {
- logger.fine("loseFocus");
- if (app != null) {
- app.loseFocus();
- }
-
- if (view != null) {
- view.onPause();
- }
-
- if (app != null) {
- //pause the audio
- AudioRenderer audioRenderer = app.getAudioRenderer();
- if (audioRenderer != null) {
- audioRenderer.pauseAll();
- }
- //pause the sensors (aka joysticks)
- if (app.getContext() != null) {
- JoyInput joyInput = app.getContext().getJoyInput();
- if (joyInput != null) {
- if (joyInput instanceof AndroidSensorJoyInput) {
- AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
- androidJoyInput.pauseSensors();
- }
- }
- }
- }
- isGLThreadPaused = true;
- }
-}
+package com.jme3.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.ActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.*;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.input.JoyInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.android.AndroidSensorJoyInput;
+import com.jme3.input.controls.TouchListener;
+import com.jme3.input.controls.TouchTrigger;
+import com.jme3.input.event.TouchEvent;
+import com.jme3.system.AppSettings;
+import com.jme3.system.SystemListener;
+import com.jme3.system.android.JmeAndroidSystem;
+import com.jme3.system.android.OGLESContext;
+import com.jme3.util.AndroidLogHandler;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * AndroidHarness wraps a jme application object and runs it on
+ * Android
+ *
+ * @author Kirill
+ * @author larynx
+ */
+public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener {
+
+ protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
+ /**
+ * The application class to start
+ */
+ protected String appClass = "jme3test.android.Test";
+ /**
+ * The jme3 application object
+ */
+ protected Application app = null;
+
+ /**
+ * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
+ * (default = 24)
+ */
+ protected int eglBitsPerPixel = 24;
+
+ /**
+ * Sets the desired number of Alpha bits for the surfaceview. This affects
+ * how the surfaceview is able to display Android views that are located
+ * under the surfaceview jME uses to render the scenegraph.
+ * 0 = Opaque surfaceview background (fastest)
+ * 1->7 = Transparent surfaceview background
+ * 8 or higher = Translucent surfaceview background
+ * (default = 0)
+ */
+ protected int eglAlphaBits = 0;
+
+ /**
+ * The number of depth bits specifies the precision of the depth buffer.
+ * (default = 16)
+ */
+ protected int eglDepthBits = 16;
+
+ /**
+ * Sets the number of samples to use for multisampling.
+ * Leave 0 (default) to disable multisampling.
+ * Set to 2 or 4 to enable multisampling.
+ */
+ protected int eglSamples = 0;
+
+ /**
+ * Set the number of stencil bits.
+ * (default = 0)
+ */
+ protected int eglStencilBits = 0;
+
+ /**
+ * Set the desired frame rate. If frameRate > 0, the application
+ * will be capped at the desired frame rate.
+ * (default = -1, no frame rate cap)
+ */
+ protected int frameRate = -1;
+
+ /**
+ * Sets the type of Audio Renderer to be used.
+ *
+ * Android MediaPlayer / SoundPool can be used on all
+ * supported Android platform versions (2.2+)
+ * OpenAL Soft uses an OpenSL backend and is only supported on Android
+ * versions 2.3+.
+ *
+ * Only use ANDROID_ static strings found in AppSettings + * + */ + protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT; + + /** + * If true Android Sensors are used as simulated Joysticks. Users can use the + * Android sensor feedback through the RawInputListener or by registering + * JoyAxisTriggers. + */ + protected boolean joystickEventsEnabled = false; + /** + * If true KeyEvents are generated from TouchEvents + */ + protected boolean keyEventsEnabled = true; + /** + * If true MouseEvents are generated from TouchEvents + */ + protected boolean mouseEventsEnabled = true; + /** + * Flip X axis + */ + protected boolean mouseEventsInvertX = false; + /** + * Flip Y axis + */ + protected boolean mouseEventsInvertY = false; + /** + * if true finish this activity when the jme app is stopped + */ + protected boolean finishOnAppStop = true; + /** + * set to false if you don't want the harness to handle the exit hook + */ + protected boolean handleExitHook = true; + /** + * Title of the exit dialog, default is "Do you want to exit?" + */ + protected String exitDialogTitle = "Do you want to exit?"; + /** + * Message of the exit dialog, default is "Use your home key to bring this + * app into the background or exit to terminate it." + */ + protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it."; + /** + * Set the screen window mode. If screenFullSize is true, then the + * notification bar and title bar are removed and the screen covers the + * entire display. If screenFullSize is false, then the notification bar + * remains visible if screenShowTitle is true while screenFullScreen is + * false, then the title bar is also displayed under the notification bar. + */ + protected boolean screenFullScreen = true; + /** + * if screenShowTitle is true while screenFullScreen is false, then the + * title bar is also displayed under the notification bar + */ + protected boolean screenShowTitle = true; + /** + * Splash Screen picture Resource ID. If a Splash Screen is desired, set + * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If + * splashPicID = 0, then no splash screen will be displayed. + */ + protected int splashPicID = 0; + + + protected OGLESContext ctx; + protected GLSurfaceView view = null; + protected boolean isGLThreadPaused = true; + protected ImageView splashImageView = null; + protected FrameLayout frameLayout = null; + final private String ESCAPE_EVENT = "TouchEscape"; + private boolean firstDrawFrame = true; + private boolean inConfigChange = false; + + private class DataObject { + protected Application app = null; + } + + @Override + public Object onRetainNonConfigurationInstance() { + logger.log(Level.FINE, "onRetainNonConfigurationInstance"); + final DataObject data = new DataObject(); + data.app = this.app; + inConfigChange = true; + return data; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + initializeLogHandler(); + + logger.fine("onCreate"); + super.onCreate(savedInstanceState); + + if (screenFullScreen) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + if (!screenShowTitle) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + } + + final DataObject data = (DataObject) getLastNonConfigurationInstance(); + if (data != null) { + logger.log(Level.FINE, "Using Retained App"); + this.app = data.app; + } else { + // Discover the screen reolution + //TODO try to find a better way to get a hand on the resolution + WindowManager wind = this.getWindowManager(); + Display disp = wind.getDefaultDisplay(); + Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight()); + + // Create Settings + logger.log(Level.FINE, "Creating settings"); + AppSettings settings = new AppSettings(true); + settings.setEmulateMouse(mouseEventsEnabled); + settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY); + settings.setUseJoysticks(joystickEventsEnabled); + settings.setEmulateKeyboard(keyEventsEnabled); + + settings.setBitsPerPixel(eglBitsPerPixel); + settings.setAlphaBits(eglAlphaBits); + settings.setDepthBits(eglDepthBits); + settings.setSamples(eglSamples); + settings.setStencilBits(eglStencilBits); + + settings.setResolution(disp.getWidth(), disp.getHeight()); + settings.setAudioRenderer(audioRendererType); + + settings.setFrameRate(frameRate); + + // Create application instance + try { + if (app == null) { + @SuppressWarnings("unchecked") + Class extends Application> clazz = (Class extends Application>) Class.forName(appClass); + app = clazz.newInstance(); + } + + app.setSettings(settings); + app.start(); + } catch (Exception ex) { + handleError("Class " + appClass + " init failed", ex); + setContentView(new TextView(this)); + } + } + + ctx = (OGLESContext) app.getContext(); + view = ctx.createView(this); + // store the glSurfaceView in JmeAndroidSystem for future use + JmeAndroidSystem.setView(view); + // AndroidHarness wraps the app as a SystemListener. + ctx.setSystemListener(this); + layoutDisplay(); + } + + @Override + protected void onRestart() { + logger.fine("onRestart"); + super.onRestart(); + if (app != null) { + app.restart(); + } + } + + @Override + protected void onStart() { + logger.fine("onStart"); + super.onStart(); + } + + @Override + protected void onResume() { + logger.fine("onResume"); + super.onResume(); + + gainFocus(); + } + + @Override + protected void onPause() { + logger.fine("onPause"); + loseFocus(); + + super.onPause(); + } + + @Override + protected void onStop() { + logger.fine("onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + logger.fine("onDestroy"); + final DataObject data = (DataObject) getLastNonConfigurationInstance(); + if (data != null || inConfigChange) { + logger.fine("In Config Change, not stopping app."); + } else { + if (app != null) { + app.stop(!isGLThreadPaused); + } + } + setContentView(new TextView(this)); + ctx = null; + app = null; + view = null; + JmeAndroidSystem.setView(null); + + super.onDestroy(); + } + + public Application getJmeApplication() { + return app; + } + + /** + * Called when an error has occurred. By default, will show an error message + * to the user and print the exception/error to the log. + */ + @Override + public void handleError(final String errorMsg, final Throwable t) { + String stackTrace = ""; + String title = "Error"; + + if (t != null) { + // Convert exception to string + StringWriter sw = new StringWriter(100); + t.printStackTrace(new PrintWriter(sw)); + stackTrace = sw.toString(); + title = t.toString(); + } + + final String finalTitle = title; + final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception") + + "\n" + stackTrace; + + logger.log(Level.SEVERE, finalMsg); + + runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) + .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create(); + dialog.show(); + } + }); + } + + /** + * Called by the android alert dialog, terminate the activity and OpenGL + * rendering + * + * @param dialog + * @param whichButton + */ + public void onClick(DialogInterface dialog, int whichButton) { + if (whichButton != -2) { + if (app != null) { + app.stop(true); + } + app = null; + this.finish(); + } + } + + /** + * Gets called by the InputManager on all touch/drag/scale events + */ + @Override + public void onTouch(String name, TouchEvent evt, float tpf) { + if (name.equals(ESCAPE_EVENT)) { + switch (evt.getType()) { + case KEY_UP: + runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) + .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create(); + dialog.show(); + } + }); + break; + default: + break; + } + } + } + + public void layoutDisplay() { + logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); + if (view == null) { + logger.log(Level.FINE, "view is null!"); + } + if (splashPicID != 0) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + Gravity.CENTER); + + frameLayout = new FrameLayout(this); + splashImageView = new ImageView(this); + + Drawable drawable = this.getResources().getDrawable(splashPicID); + if (drawable instanceof NinePatchDrawable) { + splashImageView.setBackgroundDrawable(drawable); + } else { + splashImageView.setImageResource(splashPicID); + } + + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + frameLayout.addView(view); + + if (splashImageView.getParent() != null) { + ((ViewGroup) splashImageView.getParent()).removeView(splashImageView); + } + frameLayout.addView(splashImageView, lp); + + setContentView(frameLayout); + logger.log(Level.FINE, "Splash Screen Created"); + } else { + logger.log(Level.FINE, "Splash Screen Skipped."); + setContentView(view); + } + } + + public void removeSplashScreen() { + logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); + if (splashPicID != 0) { + if (frameLayout != null) { + if (splashImageView != null) { + this.runOnUiThread(new Runnable() { + @Override + public void run() { + splashImageView.setVisibility(View.INVISIBLE); + frameLayout.removeView(splashImageView); + } + }); + } else { + logger.log(Level.FINE, "splashImageView is null"); + } + } else { + logger.log(Level.FINE, "frameLayout is null"); + } + } + } + + /** + * Removes the standard Android log handler due to an issue with not logging + * entries lower than INFO level and adds a handler that produces + * JME formatted log messages. + */ + protected void initializeLogHandler() { + Logger log = LogManager.getLogManager().getLogger(""); + for (Handler handler : log.getHandlers()) { + if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) { + Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName()); + } + log.removeHandler(handler); + } + Handler handler = new AndroidLogHandler(); + log.addHandler(handler); + handler.setLevel(Level.ALL); + } + + public void initialize() { + app.initialize(); + if (handleExitHook) { + // remove existing mapping from SimpleApplication that stops the app + // when the esc key is pressed (esc key = android back key) so that + // AndroidHarness can produce the exit app dialog box. + if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) { + app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT); + } + + app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK)); + app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT}); + } + } + + public void reshape(int width, int height) { + app.reshape(width, height); + } + + public void update() { + app.update(); + // call to remove the splash screen, if present. + // call after app.update() to make sure no gap between + // splash screen going away and app display being shown. + if (firstDrawFrame) { + removeSplashScreen(); + firstDrawFrame = false; + } + } + + public void requestClose(boolean esc) { + app.requestClose(esc); + } + + public void destroy() { + if (app != null) { + app.destroy(); + } + if (finishOnAppStop) { + finish(); + } + } + + public void gainFocus() { + logger.fine("gainFocus"); + if (view != null) { + view.onResume(); + } + + if (app != null) { + //resume the audio + AudioRenderer audioRenderer = app.getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.resumeAll(); + } + //resume the sensors (aka joysticks) + if (app.getContext() != null) { + JoyInput joyInput = app.getContext().getJoyInput(); + if (joyInput != null) { + if (joyInput instanceof AndroidSensorJoyInput) { + AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; + androidJoyInput.resumeSensors(); + } + } + } + } + + isGLThreadPaused = false; + + if (app != null) { + app.gainFocus(); + } + } + + public void loseFocus() { + logger.fine("loseFocus"); + if (app != null) { + app.loseFocus(); + } + + if (view != null) { + view.onPause(); + } + + if (app != null) { + //pause the audio + AudioRenderer audioRenderer = app.getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.pauseAll(); + } + //pause the sensors (aka joysticks) + if (app.getContext() != null) { + JoyInput joyInput = app.getContext().getJoyInput(); + if (joyInput != null) { + if (joyInput instanceof AndroidSensorJoyInput) { + AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; + androidJoyInput.pauseSensors(); + } + } + } + } + isGLThreadPaused = true; + } +}