|
@@ -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;
|
|
|
-
|
|
|
-/**
|
|
|
- * <code>AndroidHarness</code> 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.</br>
|
|
|
- * Leave 0 (default) to disable multisampling.</br>
|
|
|
- * 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.
|
|
|
- * <p>
|
|
|
- * Android MediaPlayer / SoundPool can be used on all
|
|
|
- * supported Android platform versions (2.2+)<br>
|
|
|
- * OpenAL Soft uses an OpenSL backend and is only supported on Android
|
|
|
- * versions 2.3+.
|
|
|
- * <p>
|
|
|
- * 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;
|
|
|
+
|
|
|
+/**
|
|
|
+ * <code>AndroidHarness</code> 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.</br>
|
|
|
+ * Leave 0 (default) to disable multisampling.</br>
|
|
|
+ * 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.
|
|
|
+ * <p>
|
|
|
+ * Android MediaPlayer / SoundPool can be used on all
|
|
|
+ * supported Android platform versions (2.2+)<br>
|
|
|
+ * OpenAL Soft uses an OpenSL backend and is only supported on Android
|
|
|
+ * versions 2.3+.
|
|
|
+ * <p>
|
|
|
+ * 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;
|
|
|
+ }
|
|
|
+}
|