From 1569e9583a9a5c0c0ec317df8ac44424445f88a4 Mon Sep 17 00:00:00 2001 From: iwgeric Date: Thu, 2 Apr 2015 13:08:53 -0400 Subject: [PATCH] Android: remove deprecated screenOrientation from AndroidHarness. Screen orientation is set in manifest instead of in MainActivity. --- .../java/com/jme3/app/AndroidHarness.java | 1166 ++++++++--------- 1 file changed, 580 insertions(+), 586 deletions(-) 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 clazz = (Class) 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 clazz = (Class) 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; + } +}